1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2012 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, September MM
25  */
26 /**
27  * @file
28  * User Agent Commands
29  */
30 
31 #include "include/bareos.h"
32 #include "dird.h"
33 #include "dird/dird_globals.h"
34 #include "dird/backup.h"
35 #include "dird/ua_cmdstruct.h"
36 #include "dird/expand.h"
37 #include "dird/fd_cmds.h"
38 #include "dird/jcr_private.h"
39 #include "dird/job.h"
40 #include "dird/next_vol.h"
41 #include "dird/sd_cmds.h"
42 #include "dird/storage.h"
43 #include "dird/ua_db.h"
44 #include "dird/ua_impexp.h"
45 #include "dird/ua_input.h"
46 #include "dird/ua_label.h"
47 #include "dird/ua_select.h"
48 #include "dird/ua_status.h"
49 #include "dird/ua_purge.h"
50 #include "dird/ua_run.h"
51 #include "include/auth_protocol_types.h"
52 #include "lib/bnet.h"
53 #include "lib/edit.h"
54 #include "lib/parse_conf.h"
55 #include "lib/util.h"
56 
57 namespace directordaemon {
58 
59 /* Imported subroutines */
60 
61 /* Imported variables */
62 
63 /*
64  * Imported functions
65  */
66 
67 /* ua_cmds.c */
68 extern bool AutodisplayCmd(UaContext* ua, const char* cmd);
69 extern bool ConfigureCmd(UaContext* ua, const char* cmd);
70 extern bool gui_cmd(UaContext* ua, const char* cmd);
71 extern bool LabelCmd(UaContext* ua, const char* cmd);
72 extern bool list_cmd(UaContext* ua, const char* cmd);
73 extern bool LlistCmd(UaContext* ua, const char* cmd);
74 extern bool MessagesCmd(UaContext* ua, const char* cmd);
75 extern bool PruneCmd(UaContext* ua, const char* cmd);
76 extern bool PurgeCmd(UaContext* ua, const char* cmd);
77 extern bool QueryCmd(UaContext* ua, const char* cmd);
78 extern bool RelabelCmd(UaContext* ua, const char* cmd);
79 extern bool RestoreCmd(UaContext* ua, const char* cmd);
80 extern bool show_cmd(UaContext* ua, const char* cmd);
81 extern bool SqlqueryCmd(UaContext* ua, const char* cmd);
82 extern bool StatusCmd(UaContext* ua, const char* cmd);
83 extern bool UpdateCmd(UaContext* ua, const char* cmd);
84 
85 /* ua_dotcmds.c */
86 extern bool DotCatalogsCmd(UaContext* ua, const char* cmd);
87 extern bool DotAdminCmds(UaContext* ua, const char* cmd);
88 extern bool DotJobdefsCmd(UaContext* ua, const char* cmd);
89 extern bool DotJobsCmd(UaContext* ua, const char* cmd);
90 extern bool DotJobstatusCmd(UaContext* ua, const char* cmd);
91 extern bool DotFilesetsCmd(UaContext* ua, const char* cmd);
92 extern bool DotClientsCmd(UaContext* ua, const char* cmd);
93 extern bool DotConsolesCmd(UaContext* ua, const char* cmd);
94 extern bool DotUsersCmd(UaContext* ua, const char* cmd);
95 extern bool DotMsgsCmd(UaContext* ua, const char* cmd);
96 extern bool DotPoolsCmd(UaContext* ua, const char* cmd);
97 extern bool DotScheduleCmd(UaContext* ua, const char* cmd);
98 extern bool DotStorageCmd(UaContext* ua, const char* cmd);
99 extern bool DotDefaultsCmd(UaContext* ua, const char* cmd);
100 extern bool DotTypesCmd(UaContext* ua, const char* cmd);
101 extern bool DotLevelsCmd(UaContext* ua, const char* cmd);
102 extern bool DotGetmsgsCmd(UaContext* ua, const char* cmd);
103 extern bool DotVolstatusCmd(UaContext* ua, const char* cmd);
104 extern bool DotMediatypesCmd(UaContext* ua, const char* cmd);
105 extern bool DotLocationsCmd(UaContext* ua, const char* cmd);
106 extern bool DotMediaCmd(UaContext* ua, const char* cmd);
107 extern bool DotProfilesCmd(UaContext* ua, const char* cmd);
108 extern bool DotAopCmd(UaContext* ua, const char* cmd);
109 extern bool DotBvfsLsdirsCmd(UaContext* ua, const char* cmd);
110 extern bool DotBvfsLsfilesCmd(UaContext* ua, const char* cmd);
111 extern bool DotBvfsUpdateCmd(UaContext* ua, const char* cmd);
112 extern bool DotBvfsGetJobidsCmd(UaContext* ua, const char* cmd);
113 extern bool DotBvfsVersionsCmd(UaContext* ua, const char* cmd);
114 extern bool DotBvfsRestoreCmd(UaContext* ua, const char* cmd);
115 extern bool DotBvfsCleanupCmd(UaContext* ua, const char* cmd);
116 extern bool DotBvfsClearCacheCmd(UaContext* ua, const char* cmd);
117 extern bool DotApiCmd(UaContext* ua, const char* cmd);
118 extern bool DotSqlCmd(UaContext* ua, const char* cmd);
119 extern bool DotAuthorizedCmd(UaContext* ua, const char* cmd);
120 
121 /* ua_status.c */
122 extern bool DotStatusCmd(UaContext* ua, const char* cmd);
123 
124 /* Forward referenced functions */
125 static bool add_cmd(UaContext* ua, const char* cmd);
126 static bool AutomountCmd(UaContext* ua, const char* cmd);
127 static bool CancelCmd(UaContext* ua, const char* cmd);
128 static bool CreateCmd(UaContext* ua, const char* cmd);
129 static bool DeleteCmd(UaContext* ua, const char* cmd);
130 static bool DisableCmd(UaContext* ua, const char* cmd);
131 static bool EnableCmd(UaContext* ua, const char* cmd);
132 static bool EstimateCmd(UaContext* ua, const char* cmd);
133 static bool help_cmd(UaContext* ua, const char* cmd);
134 static bool DotHelpCmd(UaContext* ua, const char* cmd);
135 static bool MemoryCmd(UaContext* ua, const char* cmd);
136 static bool MountCmd(UaContext* ua, const char* cmd);
137 static bool noop_cmd(UaContext* ua, const char* cmd);
138 static bool ReleaseCmd(UaContext* ua, const char* cmd);
139 static bool ReloadCmd(UaContext* ua, const char* cmd);
140 static bool ResolveCmd(UaContext* ua, const char* cmd);
141 static bool SetdebugCmd(UaContext* ua, const char* cmd);
142 static bool SetbwlimitCmd(UaContext* ua, const char* cmd);
143 static bool SetipCmd(UaContext* ua, const char* cmd);
144 static bool time_cmd(UaContext* ua, const char* cmd);
145 static bool TraceCmd(UaContext* ua, const char* cmd);
146 static bool TruncateCmd(UaContext* ua, const char* cmd);
147 static bool UnmountCmd(UaContext* ua, const char* cmd);
148 static bool use_cmd(UaContext* ua, const char* cmd);
149 static bool var_cmd(UaContext* ua, const char* cmd);
150 static bool VersionCmd(UaContext* ua, const char* cmd);
151 static bool wait_cmd(UaContext* ua, const char* cmd);
152 static bool WhoAmICmd(UaContext* ua, const char* cmd);
153 
154 static void DoJobDelete(UaContext* ua, JobId_t JobId);
155 static bool DeleteJobIdRange(UaContext* ua, char* tok);
156 static bool DeleteVolume(UaContext* ua);
157 static bool DeletePool(UaContext* ua);
158 static void DeleteJob(UaContext* ua);
159 static bool DoTruncate(UaContext* ua, MediaDbRecord& mr);
160 
161 bool quit_cmd(UaContext* ua, const char* cmd);
162 
163 /**
164  * Not all in alphabetical order.
165  * New commands are added after existing commands with similar letters
166  * to prevent breakage of existing user scripts.
167  */
168 static struct ua_cmdstruct commands[] = {
169     {NT_("."), noop_cmd, _("no op"), NULL, true, false},
170     {NT_(".actiononpurge"), DotAopCmd, _("List possible actions on purge"),
171      NULL, true, false},
172     {NT_(".api"), DotApiCmd, _("Switch between different api modes"),
173      NT_("[ 0 | 1 | 2 | off | on | json ] [compact=<yes|no>]"), false, false},
174     {NT_(".authorized"), DotAuthorizedCmd, _("Check for authorization"),
175      NT_("job=<job-name> | client=<client-name> | storage=<storage-name | \n"
176          "schedule=<schedule-name> | pool=<pool-name> | cmd=<command> | \n"
177          "fileset=<fileset-name> | catalog=<catalog>"),
178      false, false},
179     {NT_(".catalogs"), DotCatalogsCmd, _("List all catalog resources"), NULL,
180      false, false},
181     {NT_(".clients"), DotClientsCmd, _("List all client resources"),
182      NT_("[enabled | disabled]"), true, false},
183     {NT_(".consoles"), DotConsolesCmd, _("List all console resources"), NULL,
184      true, false},
185     {NT_(".users"), DotUsersCmd, _("List all user resources"), NULL, true,
186      false},
187     {NT_(".defaults"), DotDefaultsCmd, _("Get default settings"),
188      NT_("job=<job-name> | client=<client-name> | storage=<storage-name | "
189          "pool=<pool-name>"),
190      false, false},
191 #ifdef DEVELOPER
192     {NT_(".die"), DotAdminCmds, _("Generate Segmentation Fault"),
193      NT_("[dir | director] [client=<client>] [storage=<storage>]"), false,
194      true},
195     {NT_(".dump"), DotAdminCmds, _("Dump memory statistics"),
196      NT_("[dir | director] [client=<client>] [storage=<storage>]"), false,
197      true},
198     {NT_(".memcheck"), DotAdminCmds, _("Checks for internal memory leaks"),
199      NT_("[dir | director] [client=<client>] [storage=<storage>]"), false,
200      true},
201     {NT_(".exit"), DotAdminCmds, _("Close connection"),
202      NT_("[dir | director] [client=<client>] [storage=<storage>]"), false,
203      true},
204 #endif
205     {NT_(".filesets"), DotFilesetsCmd, _("List all filesets"), NULL, false,
206      false},
207     {NT_(".help"), DotHelpCmd, _("Print parsable information about a command"),
208      NT_("[ all | item=cmd ]"), false, false},
209     {NT_(".jobdefs"), DotJobdefsCmd, _("List all job defaults resources"), NULL,
210      true, false},
211     {NT_(".jobs"), DotJobsCmd, _("List all job resources"),
212      NT_("[type=<jobtype>] | [enabled | disabled]"), true, false},
213     {NT_(".jobstatus"), DotJobstatusCmd, _("List jobstatus information"),
214      NT_("[ =<jobstatus> ]"), true, false},
215     {NT_(".levels"), DotLevelsCmd, _("List all backup levels"), NULL, false,
216      false},
217     {NT_(".locations"), DotLocationsCmd, NULL, NULL, true, false},
218     {NT_(".messages"), DotGetmsgsCmd, _("Display pending messages"), NULL,
219      false, false},
220     {NT_(".media"), DotMediaCmd, _("List all medias"), NULL, true, false},
221     {NT_(".mediatypes"), DotMediatypesCmd, _("List all media types"), NULL,
222      true, false},
223     {NT_(".msgs"), DotMsgsCmd, _("List all message resources"), NULL, false,
224      false},
225     {NT_(".pools"), DotPoolsCmd, _("List all pool resources"),
226      NT_("type=<pooltype>"), true, false},
227     {NT_(".profiles"), DotProfilesCmd, _("List all profile resources"), NULL,
228      true, false},
229     {NT_(".quit"), quit_cmd, _("Close connection"), NULL, false, false},
230     {NT_(".sql"), DotSqlCmd, _("Send an arbitrary SQL command"),
231      NT_("query=<sqlquery>"), false, true},
232     {NT_(".schedule"), DotScheduleCmd, _("List all schedule resources"),
233      NT_("[enabled | disabled]"), false, false},
234     {NT_(".status"), DotStatusCmd, _("Report status"),
235      NT_("dir ( current | last | header | scheduled | running | terminated ) "
236          "|\n"
237          "storage=<storage> [ header | waitreservation | devices | volumes | "
238          "spooling | running | terminated ] |\n"
239          "client=<client> [ header | terminated | running ]"),
240      false, true},
241     {NT_(".storages"), DotStorageCmd, _("List all storage resources"),
242      NT_("[enabled | disabled]"), true, false},
243     {NT_(".types"), DotTypesCmd, _("List all job types"), NULL, false, false},
244     {NT_(".volstatus"), DotVolstatusCmd, _("List all volume status"), NULL,
245      true, false},
246     {NT_(".bvfs_lsdirs"), DotBvfsLsdirsCmd, _("List directories using BVFS"),
247      NT_("jobid=<jobid> path=<path> | pathid=<pathid> [limit=<limit>] "
248          "[offset=<offset>]"),
249      true, true},
250     {NT_(".bvfs_lsfiles"), DotBvfsLsfilesCmd, _("List files using BVFS"),
251      NT_("jobid=<jobid> path=<path> | pathid=<pathid> [limit=<limit>] "
252          "[offset=<offset>]"),
253      true, true},
254     {NT_(".bvfs_update"), DotBvfsUpdateCmd, _("Update BVFS cache"),
255      NT_("[jobid=<jobid>]"), true, true},
256     {NT_(".bvfs_get_jobids"), DotBvfsGetJobidsCmd,
257      _("Get jobids required for a restore"),
258      NT_("jobid=<jobid> | ujobid=<unique-jobid> [all]"), true, true},
259     {NT_(".bvfs_versions"), DotBvfsVersionsCmd, _("List versions of a file"),
260      NT_("jobid=0 client=<client-name> pathid=<path-id> filename=<file-name> "
261          "[copies] [versions]"),
262      true, true},
263     {NT_(".bvfs_restore"), DotBvfsRestoreCmd,
264      _("Mark BVFS files/directories for restore. Stored in handle."),
265      NT_("path=<handle> jobid=<jobid> [fileid=<file-id>] [dirid=<dirid>] "
266          "[hardlink=<hardlink>]"),
267      true, true},
268     {NT_(".bvfs_cleanup"), DotBvfsCleanupCmd,
269      _("Cleanup BVFS cache for a certain handle"), NT_("path=<handle>"), true,
270      true},
271     {NT_(".bvfs_clear_cache"), DotBvfsClearCacheCmd, _("Clear BVFS cache"),
272      NT_("yes"), false, true},
273     {NT_("add"), add_cmd, _("Add media to a pool"),
274      NT_("pool=<pool-name> storage=<storage-name> jobid=<jobid>"), false, true},
275     {NT_("autodisplay"), AutodisplayCmd, _("Autodisplay console messages"),
276      NT_("on | off"), false, false},
277     {NT_("automount"), AutomountCmd, _("Automount after label"),
278      NT_("on | off"), false, true},
279     {NT_("cancel"), CancelCmd, _("Cancel a job"),
280      NT_("storage=<storage-name> | jobid=<jobid> | job=<job-name> | "
281          "ujobid=<unique-jobid> | state=<job_state> | all yes"),
282      false, true},
283     {NT_("configure"), ConfigureCmd, _("Configure director resources"),
284      NT_(GetUsageStringForConsoleConfigureCommand()), false, true},
285     {NT_("create"), CreateCmd, _("Create DB Pool from resource"),
286      NT_("pool=<pool-name>"), false, true},
287     {NT_("delete"), DeleteCmd, _("Delete volume, pool or job"),
288      NT_("volume=<vol-name> pool=<pool-name> jobid=<jobid>"), true, true},
289     {NT_("disable"), DisableCmd, _("Disable a job/client/schedule"),
290      NT_("job=<job-name> client=<client-name> schedule=<schedule-name>"), true,
291      true},
292     {NT_("enable"), EnableCmd, _("Enable a job/client/schedule"),
293      NT_("job=<job-name> client=<client-name> schedule=<schedule-name>"), true,
294      true},
295     {NT_("estimate"), EstimateCmd,
296      _("Performs FileSet estimate, listing gives full listing"),
297      NT_("fileset=<fileset-name> client=<client-name> level=<level> "
298          "accurate=<yes/no> job=<job-name> listing"),
299      true, true},
300     {NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false,
301      false},
302     {NT_("export"), ExportCmd,
303      _("Export volumes from normal slots to import/export slots"),
304      NT_("storage=<storage-name> srcslots=<slot-selection> [ "
305          "dstslots=<slot-selection> volume=<volume-name> scan ]"),
306      true, true},
307     {NT_("gui"), gui_cmd,
308      _("Switch between interactive (gui off) and non-interactive (gui on) "
309        "mode"),
310      NT_("on | off"), false, false},
311     {NT_("help"), help_cmd, _("Print help on specific command"),
312      NT_("add autodisplay automount cancel configure create delete disable\n"
313          "\tenable estimate exit gui label list llist\n"
314          "\tmessages memory mount prune purge quit query\n"
315          "\trestore relabel release reload run status\n"
316          "\tsetbandwidth setdebug setip show sqlquery time trace truncate "
317          "unmount\n"
318          "\tumount update use var version wait"),
319      false, false},
320     {NT_("import"), ImportCmd,
321      _("Import volumes from import/export slots to normal slots"),
322      NT_("storage=<storage-name> [ srcslots=<slot-selection> "
323          "dstslots=<slot-selection> volume=<volume-name> scan ]"),
324      true, true},
325     {NT_("label"), LabelCmd, _("Label a tape"),
326      NT_("storage=<storage-name> volume=<volume-name> pool=<pool-name> "
327          "slot=<slot> [ drive = <drivenum>] [ barcodes ] [ encrypt ] [ yes ]"),
328      false, true},
329     {NT_("list"), list_cmd, _("List objects from catalog"),
330      NT_("basefiles jobid=<jobid> | basefiles ujobid=<complete_name> |\n"
331          "backups client=<client-name> [fileset=<fileset-name>] "
332          "[jobstatus=<status>] [level=<level>] [order=<asc|desc>] "
333          "[limit=<number>] |\n"
334          "clients | copies jobid=<jobid> |\n"
335          "files jobid=<jobid> | files ujobid=<complete_name> |\n"
336          "filesets |\n"
337          "fileset [ jobid=<jobid> ] | fileset [ ujobid=<complete_name> ] |\n"
338          "fileset [ filesetid=<filesetid> ] | fileset [ jobid=<jobid> ] |\n"
339          "jobs [job=<job-name>] [client=<client-name>] [jobstatus=<status>] "
340          "[joblevel=<joblevel>] [volume=<volumename>] [days=<number>] "
341          "[hours=<number>] [last] [count] |\n"
342          "job=<job-name> [client=<client-name>] [jobstatus=<status>] "
343          "[volume=<volumename>] [days=<number>] [hours=<number>] |\n"
344          "jobid=<jobid> | ujobid=<complete_name> |\n"
345          "joblog jobid=<jobid> | joblog ujobid=<complete_name> |\n"
346          "jobmedia jobid=<jobid> | jobmedia ujobid=<complete_name> |\n"
347          "jobtotals |\n"
348          "jobstatistics jobid=<jobid> |\n"
349          "log [ limit=<number> [ offset=<number> ] ] [reverse]|\n"
350          "media [ jobid=<jobid> | ujobid=<complete_name> | pool=<pool-name> | "
351          "all ] |\n"
352          "media=<media-name> |\n"
353          "nextvol job=<job-name> | nextvolume ujobid=<complete_name> |\n"
354          "pools |\n"
355          "pool=<pool-name> |\n"
356          "storages |\n"
357          "volumes [ jobid=<jobid> | ujobid=<complete_name> | pool=<pool-name> "
358          "| all ] [count] |\n"
359          "volume=<volume-name> |\n"
360          "[current] | [enabled | disabled] |\n"
361          "[limit=<number> [offset=<number>]]"),
362      true, true},
363     {NT_("llist"), LlistCmd, _("Full or long list like list command"),
364      NT_("basefiles jobid=<jobid> | basefiles ujobid=<complete_name> |\n"
365          "backups client=<client-name> [fileset=<fileset-name>] "
366          "[jobstatus=<status>] [level=<level>] [order=<asc|desc>] "
367          "[limit=<number>] [days=<number>] [hours=<number>]|\n"
368          "clients | copies jobid=<jobid> |\n"
369          "files jobid=<jobid> | files ujobid=<complete_name> |\n"
370          "filesets |\n"
371          "fileset jobid=<jobid> | fileset ujobid=<complete_name> |\n"
372          "fileset [ filesetid=<filesetid> ] | fileset [ jobid=<jobid> ] |\n"
373          "jobs [job=<job-name>] [client=<client-name>] [jobstatus=<status>] "
374          "[volume=<volumename>] [days=<number>] [hours=<number>] [last] "
375          "[count] |\n"
376          "job=<job-name> [client=<client-name>] [jobstatus=<status>] "
377          "[joblevel=<joblevel>] [volume=<volumename>] [days=<number>] "
378          "[hours=<number>] |\n"
379          "jobid=<jobid> | ujobid=<complete_name> |\n"
380          "joblog jobid=<jobid> [count] | joblog ujobid=<complete_name> [count] "
381          "|\n"
382          "jobmedia jobid=<jobid> | jobmedia ujobid=<complete_name> |\n"
383          "jobtotals |\n"
384          "media [ jobid=<jobid> | ujobid=<complete_name> | pool=<pool-name> | "
385          "all ] |\n"
386          "media=<media-name> |\n"
387          "nextvol job=<job-name> | nextvolume ujobid=<complete_name> |\n"
388          "pools |\n"
389          "pool=<pool-name> |\n"
390          "volumes [ jobid=<jobid> | ujobid=<complete_name> | pool=<pool-name> "
391          "| all ] |\n"
392          "volume=<volume-name> |\n"
393          "[ current ] | [ enabled ] | [disabled] |\n"
394          "[ limit=<num> [ offset=<number> ] ]"),
395      true, true},
396     {NT_("messages"), MessagesCmd, _("Display pending messages"), NT_(""),
397      false, false},
398     {NT_("memory"), MemoryCmd, _("Print current memory usage"), NT_(""), true,
399      false},
400     {NT_("mount"), MountCmd, _("Mount storage"),
401      NT_("storage=<storage-name> slot=<num> drive=<drivenum>\n"
402          "\tjobid=<jobid> | job=<job-name> | ujobid=<complete_name>"),
403      false, true},
404     {NT_("move"), move_cmd, _("Move slots in an autochanger"),
405      NT_("storage=<storage-name> srcslots=<slot-selection> "
406          "dstslots=<slot-selection>"),
407      true, true},
408     {NT_("prune"), PruneCmd, _("Prune records from catalog"),
409      NT_("files [client=<client>] [pool=<pool>] [yes] |\n"
410          "jobs [client=<client>] [pool=<pool>] [jobtype=<jobtype>] [yes] |\n"
411          "volume [=volume] [pool=<pool>] [yes] |\n"
412          "stats [yes] |\n"
413          "directory [=directory] [client=<client>] [recursive] [yes]"),
414      true, true},
415     {NT_("purge"), PurgeCmd, _("Purge records from catalog"),
416      NT_("[files [job=<job> | jobid=<jobid> | client=<client> | "
417          "volume=<volume>]] |\n"
418          "[jobs [client=<client> | volume=<volume>]] |\n"
419          "[volume[=<volume>] [storage=<storage>] [pool=<pool> | allpools] "
420          "[devicetype=<type>] [drive=<drivenum>] [action=<action>]] |\n"
421          "[quota [client=<client>]]"),
422      true, true},
423     {NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false,
424      false},
425     {NT_("query"), QueryCmd, _("Query catalog"), NT_(""), false, true},
426     {NT_("restore"), RestoreCmd, _("Restore files"),
427      NT_("where=</path> client=<client-name> storage=<storage-name> "
428          "bootstrap=<file>\n"
429          "\trestorejob=<job-name> comment=<text> jobid=<jobid> "
430          "fileset=<fileset-name>\n"
431          "\treplace=<always|never|ifolder|ifnewer> "
432          "pluginoptions=<plugin-options-string>\n"
433          "\tregexwhere=<regex> restoreclient=<client-name> "
434          "backupformat=<format>\n"
435          "\tpool=<pool-name> file=<filename> directory=<directory> "
436          "before=<date>\n"
437          "\tstrip_prefix=<prefix> add_prefix=<prefix> add_suffix=<suffix>\n"
438          "\tselect=<date> select before current copies done all"),
439      false, true},
440     {NT_("relabel"), RelabelCmd, _("Relabel a tape"),
441      NT_("storage=<storage-name> oldvolume=<old-volume-name>\n"
442          "\tvolume=<new-volume-name> pool=<pool-name> [ encrypt ]"),
443      false, true},
444     {NT_("release"), ReleaseCmd, _("Release storage"),
445      NT_("storage=<storage-name> [ drive=<drivenum> ] [ alldrives ]"), false,
446      true},
447     {NT_("reload"), ReloadCmd, _("Reload conf file"), NT_(""), true, true},
448     {NT_("rerun"), reRunCmd, _("Rerun a job"),
449      NT_("jobid=<jobid> | since_jobid=<jobid> [ until_jobid=<jobid> ] | "
450          "days=<nr_days> | hours=<nr_hours> | yes"),
451      false, true},
452     {NT_("resolve"), ResolveCmd, _("Resolve a hostname"),
453      NT_("client=<client-name> | storage=<storage-name> <host-name>"), false,
454      true},
455     {NT_("run"), RunCmd, _("Run a job"),
456      NT_("job=<job-name> client=<client-name> fileset=<fileset-name> "
457          "level=<level>\n"
458          "\tstorage=<storage-name> where=<directory-prefix> "
459          "when=<universal-time-specification>\n"
460          "\tpool=<pool-name> pluginoptions=<plugin-options-string> "
461          "accurate=<yes|no> comment=<text>\n"
462          "\tspooldata=<yes|no> priority=<number> jobid=<jobid> "
463          "catalog=<catalog> migrationjob=<job-name>\n"
464          "\tbackupclient=<client-name> backupformat=<format> "
465          "nextpool=<pool-name>\n"
466          "\tsince=<universal-time-specification> verifyjob=<job-name> "
467          "verifylist=<verify-list>\n"
468          "\tmigrationjob=<complete_name> yes"),
469      false, true},
470     {NT_("status"), StatusCmd, _("Report status"),
471      NT_("all | dir=<dir-name> | director | scheduler | "
472          "schedule=<schedule-name> | client=<client-name> |\n"
473          "\tstorage=<storage-name> slots | days=<nr_days> | job=<job-name> |\n"
474          "\tsubscriptions"),
475      true, true},
476     {NT_("setbandwidth"), SetbwlimitCmd, _("Sets bandwidth"),
477      NT_("client=<client-name> | storage=<storage-name> | jobid=<jobid> |\n"
478          "\tjob=<job-name> | ujobid=<unique-jobid> state=<job_state> | all\n"
479          "\tlimit=<nn-kbs> [ yes ]"),
480      true, true},
481     {NT_("setdebug"), SetdebugCmd, _("Sets debug level"),
482      NT_("level=<nn> trace=0/1 timestamp=0/1 client=<client-name> | dir | "
483          "storage=<storage-name> | all"),
484      true, true},
485     {NT_("setip"), SetipCmd, _("Sets new client address -- if authorized"),
486      NT_(""), false, true},
487     {NT_("show"), show_cmd, _("Show resource records"),
488      NT_("catalog=<catalog-name> | client=<client-name> | "
489          "console=<console-name> | "
490          "director=<director-name> | fileset=<fileset-name> | "
491          "jobdefs=<job-defaults> | job=<job-name> | "
492          "message=<message-resource-name> | "
493          "pool=<pool-name> | profile=<profile-name> | "
494          "schedule=<schedule-name> | storage=<storage-name> "
495          "|\n"
496          "catalog | clients | consoles | directors | filesets | jobdefs | jobs "
497          "| "
498          "messages | pools | profiles | schedules | storages "
499          "|\n"
500          "disabled [ clients | jobs | schedules ] "
501          "|\n"
502          "all [verbose]"),
503      true, true},
504     {NT_("sqlquery"), SqlqueryCmd, _("Use SQL to query catalog"), NT_(""),
505      false, true},
506     {NT_("time"), time_cmd, _("Print current time"), NT_(""), true, false},
507     {NT_("trace"), TraceCmd, _("Turn on/off trace to file"), NT_("on | off"),
508      true, true},
509     {NT_("truncate"), TruncateCmd, _("Truncate purged volumes"),
510      NT_("volstatus=Purged [storage=<storage>] [pool=<pool>] [volume=<volume>] "
511          "[yes]"),
512      true, true},
513     {NT_("unmount"), UnmountCmd, _("Unmount storage"),
514      NT_("storage=<storage-name> [ drive=<drivenum> ]\n"
515          "\tjobid=<jobid> | job=<job-name> | ujobid=<complete_name>"),
516      false, true},
517     {NT_("umount"), UnmountCmd,
518      _("Umount - for old-time Unix guys, see unmount"),
519      NT_("storage=<storage-name> [ drive=<drivenum> ]\n"
520          "\tjobid=<jobid> | job=<job-name> | ujobid=<complete_name>"),
521      false, true},
522     {NT_("update"), UpdateCmd,
523      _("Update volume, pool, slots, job or statistics"),
524      NT_("[volume=<volume-name> "
525          "[volstatus=<status>] [volretention=<time-def>] "
526          "actiononpurge=<action>] "
527          "[pool=<pool-name>] [recycle=<yes/no>] [slot=<number>] "
528          "[inchanger=<yes/no>]] "
529          "|\n"
530          "[pool=<pool-name> "
531          "[maxvolbytes=<size>] [maxvolfiles=<nb>] [maxvoljobs=<nb>]"
532          "[enabled=<yes/no>] [recyclepool=<pool-name>] "
533          "[actiononpurge=<action>] |\n"
534          "slots [storage=<storage-name>] [scan]] "
535          "|\n"
536          "[jobid=<jobid> [jobname=<name>] [starttime=<time-def>] "
537          "[client=<client-name>]\n"
538          "[filesetid=<fileset-id>] [jobtype=<job-type>]] "
539          "|\n"
540          "[stats "
541          "[days=<number>]]"),
542      true, true},
543     {NT_("use"), use_cmd, _("Use specific catalog"), NT_("catalog=<catalog>"),
544      false, true},
545     {NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false, true},
546     {NT_("version"), VersionCmd, _("Print Director version"), NT_(""), true,
547      false},
548     {NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
549      NT_("jobname=<name> | jobid=<jobid> | ujobid=<complete_name> | mount "
550          "[timeout=<number>]"),
551      false, false},
552     {NT_("whoami"), WhoAmICmd,
553      _("Print the user name associated with this console"), NT_(""), false,
554      false}};
555 
556 #define comsize ((int)(sizeof(commands) / sizeof(struct ua_cmdstruct)))
557 
execute(ua_cmdstruct * cmd)558 bool UaContext::execute(ua_cmdstruct* cmd)
559 {
560   SetCommandDefinition(cmd);
561   return (cmd->func)(this, this->cmd);
562 }
563 
564 /**
565  * Execute a command from the UA
566  */
Do_a_command(UaContext * ua)567 bool Do_a_command(UaContext* ua)
568 {
569   int i;
570   int len;
571   bool ok = false;
572   bool found = false;
573   BareosSocket* user = ua->UA_sock;
574 
575   Dmsg1(900, "Command: %s\n", ua->argk[0]);
576   if (ua->argc == 0) { return false; }
577 
578   while (ua->jcr->impl->res.write_storage_list &&
579          ua->jcr->impl->res.write_storage_list->size()) {
580     ua->jcr->impl->res.write_storage_list->remove(0);
581   }
582 
583   len = strlen(ua->argk[0]);
584   for (i = 0; i < comsize; i++) { /* search for command */
585     if (bstrncasecmp(ua->argk[0], commands[i].key, len)) {
586       /*
587        * Check if command permitted, but "quit" and "whoami" is always OK
588        */
589       if (!bstrcmp(ua->argk[0], NT_("quit")) &&
590           !bstrcmp(ua->argk[0], NT_("whoami")) &&
591           !ua->AclAccessOk(Command_ACL, ua->argk[0], true)) {
592         break;
593       }
594 
595       /*
596        * Check if this command is authorized in RunScript
597        */
598       if (ua->runscript && !commands[i].use_in_rs) {
599         ua->ErrorMsg(_("Can't use %s command in a runscript"), ua->argk[0]);
600         break;
601       }
602 
603       /*
604        * If we need to audit this event do it now.
605        */
606       if (ua->AuditEventWanted(commands[i].audit_event)) {
607         ua->LogAuditEventCmdline();
608       }
609 
610       /*
611        * Some commands alter the JobStatus of the JobControlRecord.
612        * As the console JobControlRecord keeps running,
613        * we set it to running state again.
614        * ua->jcr->setJobStatus(JS_Running)
615        * isn't enough, as it does not overwrite error states.
616        */
617       ua->jcr->JobStatus = JS_Running;
618 
619       if (ua->api) { user->signal(BNET_CMD_BEGIN); }
620       ua->send->SetMode(ua->api);
621       ok = ua->execute(&commands[i]);
622       if (ua->api) { user->signal(ok ? BNET_CMD_OK : BNET_CMD_FAILED); }
623 
624       found = true;
625       break;
626     }
627   }
628 
629   if (!found) {
630     ua->ErrorMsg(_("%s: is an invalid command.\n"), ua->argk[0]);
631     ok = false;
632   }
633   ua->send->FinalizeResult(ok);
634 
635   return ok;
636 }
637 
IsDotCommand(const char * cmd)638 static bool IsDotCommand(const char* cmd)
639 {
640   if (cmd && (strlen(cmd) > 0) && (cmd[0] == '.')) { return true; }
641   return false;
642 }
643 
644 /**
645  * Add Volumes to an existing Pool
646  */
add_cmd(UaContext * ua,const char * cmd)647 static bool add_cmd(UaContext* ua, const char* cmd)
648 {
649   PoolDbRecord pr;
650   MediaDbRecord mr;
651   int num, i, max, startnum;
652   char name[MAX_NAME_LENGTH];
653   StorageResource* store;
654   slot_number_t Slot = 0;
655   int8_t InChanger = 0;
656 
657   ua->SendMsg(
658       _("You probably don't want to be using this command since it\n"
659         "creates database records without labeling the Volumes.\n"
660         "You probably want to use the \"label\" command.\n\n"));
661 
662   if (!OpenClientDb(ua)) { return true; }
663 
664   if (!GetPoolDbr(ua, &pr)) { return true; }
665 
666   Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols, pr.MaxVols,
667         pr.PoolType);
668 
669   while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
670     ua->WarningMsg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
671     if (!GetPint(ua, _("Enter new maximum (zero for unlimited): "))) {
672       return true;
673     }
674     pr.MaxVols = ua->pint32_val;
675   }
676 
677   /*
678    * Get media type
679    */
680   if ((store = get_storage_resource(ua)) != NULL) {
681     bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
682   } else if (!GetMediaType(ua, mr.MediaType, sizeof(mr.MediaType))) {
683     return true;
684   }
685 
686   if (pr.MaxVols == 0) {
687     max = 1000;
688   } else {
689     max = pr.MaxVols - pr.NumVols;
690   }
691   for (;;) {
692     char buf[100];
693 
694     Bsnprintf(buf, sizeof(buf),
695               _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "),
696               max);
697     if (!GetPint(ua, buf)) { return true; }
698     num = ua->pint32_val;
699     if (num < 0 || num > max) {
700       ua->WarningMsg(_("The number must be between 0 and %d\n"), max);
701       continue;
702     }
703     break;
704   }
705 
706   for (;;) {
707     if (num == 0) {
708       if (!GetCmd(ua, _("Enter Volume name: "))) { return true; }
709     } else {
710       if (!GetCmd(ua, _("Enter base volume name: "))) { return true; }
711     }
712 
713     /*
714      * Don't allow | in Volume name because it is the volume separator character
715      */
716     if (!IsVolumeNameLegal(ua, ua->cmd)) { continue; }
717     if (strlen(ua->cmd) >= MAX_NAME_LENGTH - 10) {
718       ua->WarningMsg(_("Volume name too long.\n"));
719       continue;
720     }
721     if (strlen(ua->cmd) == 0) {
722       ua->WarningMsg(_("Volume name must be at least one character long.\n"));
723       continue;
724     }
725     break;
726   }
727 
728   bstrncpy(name, ua->cmd, sizeof(name));
729   if (num > 0) {
730     bstrncat(name, "%04d", sizeof(name));
731 
732     for (;;) {
733       if (!GetPint(ua, _("Enter the starting number: "))) { return true; }
734       startnum = ua->pint32_val;
735       if (startnum < 1) {
736         ua->WarningMsg(_("Start number must be greater than zero.\n"));
737         continue;
738       }
739       break;
740     }
741   } else {
742     startnum = 1;
743     num = 1;
744   }
745 
746   if (store && store->autochanger) {
747     if (!GetPint(ua, _("Enter slot (0 for none): "))) { return true; }
748     Slot = static_cast<slot_number_t>(ua->pint32_val);
749     if (!GetYesno(ua, _("InChanger? yes/no: "))) { return true; }
750     InChanger = ua->pint32_val;
751   }
752 
753   SetPoolDbrDefaultsInMediaDbr(&mr, &pr);
754   for (i = startnum; i < num + startnum; i++) {
755     Bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
756     mr.Slot = Slot++;
757     mr.InChanger = InChanger;
758     mr.Enabled = VOL_ENABLED;
759     SetStorageidInMr(store, &mr);
760     Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
761     if (!ua->db->CreateMediaRecord(ua->jcr, &mr)) {
762       ua->ErrorMsg("%s", ua->db->strerror());
763       return true;
764     }
765   }
766   pr.NumVols += num;
767   Dmsg0(200, "Update pool record.\n");
768   if (ua->db->UpdatePoolRecord(ua->jcr, &pr) != 1) {
769     ua->WarningMsg("%s", ua->db->strerror());
770     return true;
771   }
772   ua->SendMsg(_("%d Volumes created in pool %s\n"), num, pr.Name);
773 
774   return true;
775 }
776 
777 /**
778  * Turn auto mount on/off
779  *
780  * automount on
781  * automount off
782  */
AutomountCmd(UaContext * ua,const char * cmd)783 static bool AutomountCmd(UaContext* ua, const char* cmd)
784 {
785   char* onoff;
786 
787   if (ua->argc != 2) {
788     if (!GetCmd(ua, _("Turn on or off? "))) { return true; }
789     onoff = ua->cmd;
790   } else {
791     onoff = ua->argk[1];
792   }
793 
794   ua->automount = (Bstrcasecmp(onoff, NT_("off"))) ? 0 : 1;
795   return true;
796 }
797 
CancelStorageDaemonJob(UaContext * ua,const char * cmd)798 static inline bool CancelStorageDaemonJob(UaContext* ua, const char* cmd)
799 {
800   int i;
801   StorageResource* store;
802 
803   store = get_storage_resource(ua);
804   if (store) {
805     /*
806      * See what JobId to cancel on the storage daemon.
807      */
808     i = FindArgWithValue(ua, NT_("jobid"));
809     if (i >= 0) {
810       if (!Is_a_number(ua->argv[i])) {
811         ua->WarningMsg(_("JobId %s not a number\n"), ua->argv[i]);
812       }
813 
814       CancelStorageDaemonJob(ua, store, ua->argv[i]);
815     } else {
816       ua->WarningMsg(_("Missing jobid=JobId specification\n"));
817     }
818   }
819 
820   return true;
821 }
822 
CancelJobs(UaContext * ua,const char * cmd)823 static inline bool CancelJobs(UaContext* ua, const char* cmd)
824 {
825   JobControlRecord* jcr;
826   JobId_t* JobId = nullptr;
827   alist* selection;
828 
829   selection = select_jobs(ua, "cancel");
830   if (!selection) { return true; }
831 
832   /*
833    * Loop over the different JobIds selected.
834    */
835   foreach_alist (JobId, selection) {
836     if (!(jcr = get_jcr_by_id(*JobId))) { continue; }
837 
838     CancelJob(ua, jcr);
839     FreeJcr(jcr);
840   }
841 
842   delete selection;
843 
844   return true;
845 }
846 
847 /**
848  * Cancel a job
849  */
CancelCmd(UaContext * ua,const char * cmd)850 static bool CancelCmd(UaContext* ua, const char* cmd)
851 {
852   int i;
853 
854   /*
855    * See if we need to explicitly cancel a storage daemon Job.
856    */
857   i = FindArgWithValue(ua, NT_("storage"));
858   if (i >= 0) {
859     return CancelStorageDaemonJob(ua, cmd);
860   } else {
861     return CancelJobs(ua, cmd);
862   }
863 }
864 
865 /**
866  * Create a Pool Record in the database.
867  * It is always created from the Resource record.
868  */
CreateCmd(UaContext * ua,const char * cmd)869 static bool CreateCmd(UaContext* ua, const char* cmd)
870 {
871   PoolResource* pool;
872 
873   if (!OpenClientDb(ua)) { return true; }
874 
875   pool = get_pool_resource(ua);
876   if (!pool) { return true; }
877 
878   switch (CreatePool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
879     case 0:
880       ua->ErrorMsg(_("Error: Pool %s already exists.\n"
881                      "Use update to change it.\n"),
882                    pool->resource_name_);
883       break;
884 
885     case -1:
886       ua->ErrorMsg("%s", ua->db->strerror());
887       break;
888 
889     default:
890       break;
891   }
892   ua->SendMsg(_("Pool %s created.\n"), pool->resource_name_);
893   return true;
894 }
895 
SetbwlimitFiled(UaContext * ua,ClientResource * client,int64_t limit,char * Job)896 static inline bool SetbwlimitFiled(UaContext* ua,
897                                    ClientResource* client,
898                                    int64_t limit,
899                                    char* Job)
900 {
901   /*
902    * Connect to File daemon
903    */
904   ua->jcr->impl->res.client = client;
905   ua->jcr->max_bandwidth = limit;
906 
907   /*
908    * Try to connect for 15 seconds
909    */
910   ua->SendMsg(_("Connecting to Client %s at %s:%d\n"), client->resource_name_,
911               client->address, client->FDport);
912 
913   if (!ConnectToFileDaemon(ua->jcr, 1, 15, false)) {
914     ua->ErrorMsg(_("Failed to connect to Client.\n"));
915     return true;
916   }
917 
918   Dmsg0(120, "Connected to file daemon\n");
919   if (!SendBwlimitToFd(ua->jcr, Job)) {
920     ua->ErrorMsg(_("Failed to set bandwidth limit on Client.\n"));
921   } else {
922     ua->InfoMsg(_("OK Limiting bandwidth to %lldkb/s %s\n"), limit / 1024, Job);
923   }
924 
925   ua->jcr->file_bsock->signal(BNET_TERMINATE);
926   ua->jcr->file_bsock->close();
927   delete ua->jcr->file_bsock;
928   ua->jcr->file_bsock = NULL;
929   ua->jcr->impl->res.client = NULL;
930   ua->jcr->max_bandwidth = 0;
931 
932   return true;
933 }
934 
setbwlimit_stored(UaContext * ua,StorageResource * store,int64_t limit,char * Job)935 static inline bool setbwlimit_stored(UaContext* ua,
936                                      StorageResource* store,
937                                      int64_t limit,
938                                      char* Job)
939 {
940   /*
941    * Check the storage daemon protocol.
942    */
943   switch (store->Protocol) {
944     case APT_NDMPV2:
945     case APT_NDMPV3:
946     case APT_NDMPV4:
947       ua->ErrorMsg(
948           _("Storage selected is NDMP storage which cannot have a bandwidth "
949             "limit\n"));
950       return true;
951     default:
952       break;
953   }
954 
955   /*
956    * Connect to Storage daemon
957    */
958   ua->jcr->impl->res.write_storage = store;
959   ua->jcr->max_bandwidth = limit;
960 
961   /*
962    * Try to connect for 15 seconds
963    */
964   ua->SendMsg(_("Connecting to Storage daemon %s at %s:%d\n"),
965               store->resource_name_, store->address, store->SDport);
966 
967   if (!ConnectToStorageDaemon(ua->jcr, 1, 15, false)) {
968     ua->ErrorMsg(_("Failed to connect to Storage daemon.\n"));
969     return true;
970   }
971 
972   Dmsg0(120, "Connected to Storage daemon\n");
973   if (!SendBwlimitToFd(ua->jcr, Job)) {
974     ua->ErrorMsg(_("Failed to set bandwidth limit on Storage daemon.\n"));
975   } else {
976     ua->InfoMsg(_("OK Limiting bandwidth to %lldkb/s %s\n"), limit / 1024, Job);
977   }
978 
979   ua->jcr->store_bsock->signal(BNET_TERMINATE);
980   ua->jcr->store_bsock->close();
981   delete ua->jcr->store_bsock;
982   ua->jcr->store_bsock = NULL;
983   ua->jcr->impl->res.write_storage = NULL;
984   ua->jcr->max_bandwidth = 0;
985 
986   return true;
987 }
988 
SetbwlimitCmd(UaContext * ua,const char * cmd)989 static bool SetbwlimitCmd(UaContext* ua, const char* cmd)
990 {
991   int i;
992   int64_t limit = -1;
993   ClientResource* client = NULL;
994   StorageResource* store = NULL;
995   char Job[MAX_NAME_LENGTH];
996   const char* lst[] = {"job", "jobid", "ujobid", "all", "state", NULL};
997 
998   i = FindArgWithValue(ua, NT_("limit"));
999   if (i >= 0) { limit = ((int64_t)atoi(ua->argv[i]) * 1024); }
1000 
1001   if (limit < 0) {
1002     if (!GetPint(ua, _("Enter new bandwidth limit kb/s: "))) { return true; }
1003     limit = ((int64_t)ua->pint32_val * 1024); /* kb/s */
1004   }
1005 
1006   if (FindArgKeyword(ua, lst) > 0) {
1007     JobControlRecord* jcr;
1008     JobId_t* JobId = nullptr;
1009     alist* selection;
1010 
1011     selection = select_jobs(ua, "limit");
1012     if (!selection) { return true; }
1013 
1014     /*
1015      * Loop over the different JobIds selected.
1016      */
1017     foreach_alist (JobId, selection) {
1018       if (!(jcr = get_jcr_by_id(*JobId))) { continue; }
1019 
1020       jcr->max_bandwidth = limit;
1021       bstrncpy(Job, jcr->Job, sizeof(Job));
1022       switch (jcr->getJobType()) {
1023         case JT_COPY:
1024         case JT_MIGRATE:
1025           store = jcr->impl->res.read_storage;
1026           break;
1027         default:
1028           client = jcr->impl->res.client;
1029           break;
1030       }
1031       FreeJcr(jcr);
1032     }
1033 
1034     delete selection;
1035   } else if (FindArg(ua, NT_("storage")) >= 0) {
1036     store = get_storage_resource(ua);
1037   } else {
1038     client = get_client_resource(ua);
1039   }
1040 
1041   if (client) { return SetbwlimitFiled(ua, client, limit, Job); }
1042 
1043   if (store) { return setbwlimit_stored(ua, store, limit, Job); }
1044 
1045   return true;
1046 }
1047 
1048 /**
1049  * Set a new address in a Client resource. We do this only
1050  * if the Console name is the same as the Client name
1051  * and the Console can access the client.
1052  */
SetipCmd(UaContext * ua,const char * cmd)1053 static bool SetipCmd(UaContext* ua, const char* cmd)
1054 {
1055   ClientResource* client;
1056   char buf[1024];
1057 
1058   client = ua->GetClientResWithName(
1059       ua->user_acl->corresponding_resource->resource_name_);
1060   if (!client) {
1061     ua->ErrorMsg(_("Client \"%s\" not found.\n"),
1062                  ua->user_acl->corresponding_resource->resource_name_);
1063     return false;
1064   }
1065 
1066   if (client->address) { free(client->address); }
1067 
1068   SockaddrToAscii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
1069   client->address = strdup(buf);
1070   ua->SendMsg(_("Client \"%s\" address set to %s\n"), client->resource_name_,
1071               client->address);
1072 
1073   return true;
1074 }
1075 
DoEnDisableCmd(UaContext * ua,bool setting)1076 static void DoEnDisableCmd(UaContext* ua, bool setting)
1077 {
1078   ScheduleResource* sched = NULL;
1079   ClientResource* client = NULL;
1080   JobResource* job = NULL;
1081   int i;
1082 
1083   i = FindArg(ua, NT_("schedule"));
1084   if (i >= 0) {
1085     i = FindArgWithValue(ua, NT_("schedule"));
1086     if (i >= 0) {
1087       sched = ua->GetScheduleResWithName(ua->argv[i]);
1088     } else {
1089       sched = select_enable_disable_schedule_resource(ua, setting);
1090       if (!sched) { return; }
1091     }
1092 
1093     if (!sched) {
1094       ua->ErrorMsg(_("Schedule \"%s\" not found.\n"), ua->argv[i]);
1095       return;
1096     }
1097   } else {
1098     i = FindArg(ua, NT_("client"));
1099     if (i >= 0) {
1100       i = FindArgWithValue(ua, NT_("client"));
1101       if (i >= 0) {
1102         client = ua->GetClientResWithName(ua->argv[i]);
1103       } else {
1104         client = select_enable_disable_client_resource(ua, setting);
1105         if (!client) { return; }
1106       }
1107 
1108       if (!client) {
1109         ua->ErrorMsg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1110         return;
1111       }
1112     } else {
1113       i = FindArgWithValue(ua, NT_("job"));
1114       if (i >= 0) {
1115         job = ua->GetJobResWithName(ua->argv[i]);
1116       } else {
1117         job = select_enable_disable_job_resource(ua, setting);
1118         if (!job) { return; }
1119       }
1120 
1121       if (!job) {
1122         ua->ErrorMsg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1123         return;
1124       }
1125     }
1126   }
1127 
1128   if (sched) {
1129     sched->enabled = setting;
1130     ua->SendMsg(_("Schedule \"%s\" %sabled\n"), sched->resource_name_,
1131                 setting ? "en" : "dis");
1132   } else if (client) {
1133     client->enabled = setting;
1134     ua->SendMsg(_("Client \"%s\" %sabled\n"), client->resource_name_,
1135                 setting ? "en" : "dis");
1136   } else if (job) {
1137     job->enabled = setting;
1138     ua->SendMsg(_("Job \"%s\" %sabled\n"), job->resource_name_,
1139                 setting ? "en" : "dis");
1140   }
1141 
1142   ua->WarningMsg(
1143       _("%sabling is a temporary operation until the director reloads\n"),
1144       setting ? "En" : "Dis");
1145 }
1146 
EnableCmd(UaContext * ua,const char * cmd)1147 static bool EnableCmd(UaContext* ua, const char* cmd)
1148 {
1149   DoEnDisableCmd(ua, true);
1150   return true;
1151 }
1152 
DisableCmd(UaContext * ua,const char * cmd)1153 static bool DisableCmd(UaContext* ua, const char* cmd)
1154 {
1155   DoEnDisableCmd(ua, false);
1156   return true;
1157 }
1158 
DoStorageSetdebug(UaContext * ua,StorageResource * store,int level,int trace_flag,int timestamp_flag)1159 static void DoStorageSetdebug(UaContext* ua,
1160                               StorageResource* store,
1161                               int level,
1162                               int trace_flag,
1163                               int timestamp_flag)
1164 {
1165   BareosSocket* sd;
1166   JobControlRecord* jcr = ua->jcr;
1167   UnifiedStorageResource lstore;
1168 
1169   switch (store->Protocol) {
1170     case APT_NDMPV2:
1171     case APT_NDMPV3:
1172     case APT_NDMPV4:
1173       return;
1174     default:
1175       break;
1176   }
1177 
1178   lstore.store = store;
1179   PmStrcpy(lstore.store_source, _("unknown source"));
1180   SetWstorage(jcr, &lstore);
1181 
1182   /*
1183    * Try connecting for up to 15 seconds
1184    */
1185   ua->SendMsg(_("Connecting to Storage daemon %s at %s:%d\n"),
1186               store->resource_name_, store->address, store->SDport);
1187 
1188   if (!ConnectToStorageDaemon(jcr, 1, 15, false)) {
1189     ua->ErrorMsg(_("Failed to connect to Storage daemon.\n"));
1190     return;
1191   }
1192 
1193   Dmsg0(120, _("Connected to storage daemon\n"));
1194   sd = jcr->store_bsock;
1195   sd->fsend("setdebug=%d trace=%d timestamp=%d\n", level, trace_flag,
1196             timestamp_flag);
1197   if (sd->recv() >= 0) { ua->SendMsg("%s", sd->msg); }
1198 
1199   sd->signal(BNET_TERMINATE);
1200   sd->close();
1201   delete jcr->store_bsock;
1202   jcr->store_bsock = NULL;
1203 
1204   return;
1205 }
1206 
1207 /**
1208  * For the client, we have the following values that can be set :
1209  *
1210  * level = debug level
1211  * trace = send debug output to a file
1212  * hangup = how many records to send to SD before hanging up
1213  *          obviously this is most useful for testing restarting
1214  *          failed jobs.
1215  * timestamp = set debug msg timestamping
1216  */
DoClientSetdebug(UaContext * ua,ClientResource * client,int level,int trace_flag,int hangup_flag,int timestamp_flag)1217 static void DoClientSetdebug(UaContext* ua,
1218                              ClientResource* client,
1219                              int level,
1220                              int trace_flag,
1221                              int hangup_flag,
1222                              int timestamp_flag)
1223 {
1224   BareosSocket* fd;
1225 
1226   switch (client->Protocol) {
1227     case APT_NDMPV2:
1228     case APT_NDMPV3:
1229     case APT_NDMPV4:
1230       return;
1231     default:
1232       break;
1233   }
1234 
1235   /*
1236    * Connect to File daemon
1237    */
1238   ua->jcr->impl->res.client = client;
1239 
1240   /*
1241    * Try to connect for 15 seconds
1242    */
1243   ua->SendMsg(_("Connecting to Client %s at %s:%d\n"), client->resource_name_,
1244               client->address, client->FDport);
1245 
1246   if (!ConnectToFileDaemon(ua->jcr, 1, 15, false)) {
1247     ua->ErrorMsg(_("Failed to connect to Client.\n"));
1248     return;
1249   }
1250 
1251   Dmsg0(120, "Connected to file daemon\n");
1252   fd = ua->jcr->file_bsock;
1253   if (ua->jcr->impl->FDVersion >= FD_VERSION_53) {
1254     fd->fsend("setdebug=%d trace=%d hangup=%d timestamp=%d\n", level,
1255               trace_flag, hangup_flag, timestamp_flag);
1256   } else {
1257     fd->fsend("setdebug=%d trace=%d hangup=%d\n", level, trace_flag,
1258               hangup_flag);
1259   }
1260 
1261   if (fd->recv() >= 0) { ua->SendMsg("%s", fd->msg); }
1262 
1263   fd->signal(BNET_TERMINATE);
1264   fd->close();
1265   delete ua->jcr->file_bsock;
1266   ua->jcr->file_bsock = NULL;
1267 
1268   return;
1269 }
1270 
1271 static std::function<void(UaContext* ua,
1272                           StorageResource* store,
1273                           int level,
1274                           int trace_flag,
1275                           int timestamp_flag)>
1276     DoStorageSetdebugFunction = DoStorageSetdebug;
1277 
SetDoStorageSetdebugFunction(std::function<void (UaContext * ua,StorageResource * store,int level,int trace_flag,int timestamp_flag)> f)1278 void SetDoStorageSetdebugFunction(std::function<void(UaContext* ua,
1279                                                      StorageResource* store,
1280                                                      int level,
1281                                                      int trace_flag,
1282                                                      int timestamp_flag)> f)
1283 {
1284   // dependency injection for testing
1285   DoStorageSetdebugFunction = f;
1286 }
1287 
AllStorageSetdebug(UaContext * ua,int level,int trace_flag,int timestamp_flag)1288 static void AllStorageSetdebug(UaContext* ua,
1289                                int level,
1290                                int trace_flag,
1291                                int timestamp_flag)
1292 {
1293   std::vector<StorageResource*> storages_with_unique_address;
1294   StorageResource* storage_in_config = nullptr;
1295 
1296   LockRes(my_config);
1297   do {
1298     storage_in_config = static_cast<StorageResource*>(
1299         my_config->GetNextRes(R_STORAGE, storage_in_config));
1300 
1301     if (storage_in_config) {
1302       bool is_duplicate_address = false;
1303       for (StorageResource* compared_store : storages_with_unique_address) {
1304         if (bstrcmp(compared_store->address, storage_in_config->address) &&
1305             compared_store->SDport == storage_in_config->SDport) {
1306           is_duplicate_address = true;
1307           break;
1308         }
1309       }
1310       if (!is_duplicate_address) {
1311         storages_with_unique_address.push_back(storage_in_config);
1312         Dmsg2(140, "Stuffing: %s:%d\n", storage_in_config->address,
1313               storage_in_config->SDport);
1314       }
1315     }
1316   } while (storage_in_config);
1317   UnlockRes(my_config);
1318 
1319   for (StorageResource* store : storages_with_unique_address) {
1320     DoStorageSetdebugFunction(ua, store, level, trace_flag, timestamp_flag);
1321   }
1322 }
1323 
1324 static std::function<void(UaContext* ua,
1325                           ClientResource* client,
1326                           int level,
1327                           int trace_flag,
1328                           int hangup_flag,
1329                           int timestamp_flag)>
1330     DoClientSetdebugFunction = DoClientSetdebug;
1331 
SetDoClientSetdebugFunction(std::function<void (UaContext * ua,ClientResource * client,int level,int trace_flag,int hangup_flag,int timestamp_flag)> f)1332 void SetDoClientSetdebugFunction(std::function<void(UaContext* ua,
1333                                                     ClientResource* client,
1334                                                     int level,
1335                                                     int trace_flag,
1336                                                     int hangup_flag,
1337                                                     int timestamp_flag)> f)
1338 {
1339   // dependency injection for testing
1340   DoClientSetdebugFunction = f;
1341 }
1342 
AllClientSetdebug(UaContext * ua,int level,int trace_flag,int hangup_flag,int timestamp_flag)1343 static void AllClientSetdebug(UaContext* ua,
1344                               int level,
1345                               int trace_flag,
1346                               int hangup_flag,
1347                               int timestamp_flag)
1348 {
1349   std::vector<ClientResource*> clients_with_unique_address;
1350   ClientResource* client_in_config = nullptr;
1351 
1352   LockRes(my_config);
1353   do {
1354     client_in_config = static_cast<ClientResource*>(
1355         my_config->GetNextRes(R_CLIENT, client_in_config));
1356 
1357     if (client_in_config) {
1358       bool is_duplicate_address = false;
1359       for (ClientResource* compared_client : clients_with_unique_address) {
1360         if (bstrcmp(compared_client->address, client_in_config->address) &&
1361             compared_client->FDport == client_in_config->FDport) {
1362           is_duplicate_address = true;
1363           break;
1364         }
1365       }
1366       if (!is_duplicate_address) {
1367         clients_with_unique_address.push_back(client_in_config);
1368         Dmsg2(140, "Stuffing: %s:%d\n", client_in_config->address,
1369               client_in_config->FDport);
1370       }
1371     }
1372   } while (client_in_config);
1373   UnlockRes(my_config);
1374 
1375   for (ClientResource* client : clients_with_unique_address) {
1376     DoClientSetdebugFunction(ua, client, level, trace_flag, hangup_flag,
1377                              timestamp_flag);
1378   }
1379 }  // namespace directordaemon
1380 
DoDirectorSetdebug(UaContext * ua,int level,int trace_flag,int timestamp_flag)1381 static void DoDirectorSetdebug(UaContext* ua,
1382                                int level,
1383                                int trace_flag,
1384                                int timestamp_flag)
1385 {
1386   PoolMem tracefilename(PM_FNAME);
1387 
1388   debug_level = level;
1389   SetTrace(trace_flag);
1390   SetTimestamp(timestamp_flag);
1391   Mmsg(tracefilename, "%s/%s.trace", TRACEFILEDIRECTORY, my_name);
1392   if (ua) {
1393     ua->SendMsg("level=%d trace=%d hangup=%d timestamp=%d tracefilename=%s\n",
1394                 level, GetTrace(), GetHangup(), GetTimestamp(),
1395                 tracefilename.c_str());
1396   }
1397 }
1398 
DoAllSetDebug(UaContext * ua,int level,int trace_flag,int hangup_flag,int timestamp_flag)1399 void DoAllSetDebug(UaContext* ua,
1400                    int level,
1401                    int trace_flag,
1402                    int hangup_flag,
1403                    int timestamp_flag)
1404 {
1405   DoDirectorSetdebug(ua, level, trace_flag, timestamp_flag);
1406   AllStorageSetdebug(ua, level, trace_flag, timestamp_flag);
1407   AllClientSetdebug(ua, level, trace_flag, hangup_flag, timestamp_flag);
1408 }
1409 
1410 /**
1411  * setdebug level=nn all trace=1/0 timestamp=1/0
1412  */
SetdebugCmd(UaContext * ua,const char * cmd)1413 static bool SetdebugCmd(UaContext* ua, const char* cmd)
1414 {
1415   int i;
1416   int level;
1417   int trace_flag;
1418   int hangup_flag;
1419   int timestamp_flag;
1420   StorageResource* store;
1421   ClientResource* client;
1422 
1423   Dmsg1(120, "setdebug:%s:\n", cmd);
1424 
1425   level = -1;
1426   i = FindArgWithValue(ua, NT_("level"));
1427   if (i >= 0) { level = atoi(ua->argv[i]); }
1428   if (level < 0) {
1429     if (!GetPint(ua, _("Enter new debug level: "))) { return true; }
1430     level = ua->pint32_val;
1431   }
1432 
1433   /*
1434    * Look for trace flag. -1 => not change
1435    */
1436   i = FindArgWithValue(ua, NT_("trace"));
1437   if (i >= 0) {
1438     trace_flag = atoi(ua->argv[i]);
1439     if (trace_flag > 0) { trace_flag = 1; }
1440   } else {
1441     trace_flag = -1;
1442   }
1443 
1444   /*
1445    * Look for hangup (debug only) flag. -1 => not change
1446    */
1447   i = FindArgWithValue(ua, NT_("hangup"));
1448   if (i >= 0) {
1449     hangup_flag = atoi(ua->argv[i]);
1450   } else {
1451     hangup_flag = -1;
1452   }
1453 
1454   /*
1455    * Look for timestamp flag. -1 => not change
1456    */
1457   i = FindArgWithValue(ua, NT_("timestamp"));
1458   if (i >= 0) {
1459     timestamp_flag = atoi(ua->argv[i]);
1460     if (timestamp_flag > 0) { timestamp_flag = 1; }
1461   } else {
1462     timestamp_flag = -1;
1463   }
1464 
1465   /*
1466    * General debug?
1467    */
1468   for (i = 1; i < ua->argc; i++) {
1469     if (Bstrcasecmp(ua->argk[i], "all")) {
1470       DoAllSetDebug(ua, level, trace_flag, hangup_flag, timestamp_flag);
1471       return true;
1472     }
1473     if (Bstrcasecmp(ua->argk[i], "dir") ||
1474         Bstrcasecmp(ua->argk[i], "director")) {
1475       DoDirectorSetdebug(ua, level, trace_flag, timestamp_flag);
1476       return true;
1477     }
1478     if (Bstrcasecmp(ua->argk[i], "client") || Bstrcasecmp(ua->argk[i], "fd")) {
1479       client = NULL;
1480       if (ua->argv[i]) {
1481         client = ua->GetClientResWithName(ua->argv[i]);
1482         if (client) {
1483           DoClientSetdebug(ua, client, level, trace_flag, hangup_flag,
1484                            timestamp_flag);
1485           return true;
1486         }
1487       }
1488       client = select_client_resource(ua);
1489       if (client) {
1490         DoClientSetdebug(ua, client, level, trace_flag, hangup_flag,
1491                          timestamp_flag);
1492         return true;
1493       }
1494     }
1495 
1496     if (Bstrcasecmp(ua->argk[i], NT_("store")) ||
1497         Bstrcasecmp(ua->argk[i], NT_("storage")) ||
1498         Bstrcasecmp(ua->argk[i], NT_("sd"))) {
1499       store = NULL;
1500       if (ua->argv[i]) {
1501         store = ua->GetStoreResWithName(ua->argv[i]);
1502         if (store) {
1503           DoStorageSetdebug(ua, store, level, trace_flag, timestamp_flag);
1504           return true;
1505         }
1506       }
1507       store = get_storage_resource(ua);
1508       if (store) {
1509         switch (store->Protocol) {
1510           case APT_NDMPV2:
1511           case APT_NDMPV3:
1512           case APT_NDMPV4:
1513             ua->WarningMsg(_("Storage has non-native protocol.\n"));
1514             return true;
1515           default:
1516             break;
1517         }
1518 
1519         DoStorageSetdebug(ua, store, level, trace_flag, timestamp_flag);
1520         return true;
1521       }
1522     }
1523   }
1524 
1525   /*
1526    * We didn't find an appropriate keyword above, so prompt the user.
1527    */
1528   StartPrompt(ua, _("Available daemons are: \n"));
1529   AddPrompt(ua, _("Director"));
1530   AddPrompt(ua, _("Storage"));
1531   AddPrompt(ua, _("Client"));
1532   AddPrompt(ua, _("All"));
1533 
1534   switch (
1535       DoPrompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1536     case 0:
1537       /*
1538        * Director
1539        */
1540       DoDirectorSetdebug(ua, level, trace_flag, timestamp_flag);
1541       break;
1542     case 1:
1543       store = get_storage_resource(ua);
1544       if (store) {
1545         switch (store->Protocol) {
1546           case APT_NDMPV2:
1547           case APT_NDMPV3:
1548           case APT_NDMPV4:
1549             ua->WarningMsg(_("Storage has non-native protocol.\n"));
1550             return true;
1551           default:
1552             break;
1553         }
1554         DoStorageSetdebug(ua, store, level, trace_flag, timestamp_flag);
1555       }
1556       break;
1557     case 2:
1558       client = select_client_resource(ua);
1559       if (client) {
1560         DoClientSetdebug(ua, client, level, trace_flag, hangup_flag,
1561                          timestamp_flag);
1562       }
1563       break;
1564     case 3:
1565       DoAllSetDebug(ua, level, trace_flag, hangup_flag, timestamp_flag);
1566       break;
1567     default:
1568       break;
1569   }
1570 
1571   return true;
1572 }
1573 
1574 /**
1575  * Resolve a hostname.
1576  */
ResolveCmd(UaContext * ua,const char * cmd)1577 static bool ResolveCmd(UaContext* ua, const char* cmd)
1578 {
1579   StorageResource* storage = NULL;
1580   ClientResource* client = NULL;
1581 
1582   for (int i = 1; i < ua->argc; i++) {
1583     if (Bstrcasecmp(ua->argk[i], NT_("client")) ||
1584         Bstrcasecmp(ua->argk[i], NT_("fd"))) {
1585       if (ua->argv[i]) {
1586         client = ua->GetClientResWithName(ua->argv[i]);
1587         if (!client) {
1588           ua->ErrorMsg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1589           return true;
1590         }
1591 
1592         *ua->argk[i] = 0; /* zap keyword already visited */
1593         continue;
1594       } else {
1595         ua->ErrorMsg(_("Client name missing.\n"));
1596         return true;
1597       }
1598     } else if (Bstrcasecmp(ua->argk[i], NT_("storage"))) {
1599       if (ua->argv[i]) {
1600         storage = ua->GetStoreResWithName(ua->argv[i]);
1601         if (!storage) {
1602           ua->ErrorMsg(_("Storage \"%s\" not found.\n"), ua->argv[i]);
1603           return true;
1604         }
1605 
1606         *ua->argk[i] = 0; /* zap keyword already visited */
1607         continue;
1608       } else {
1609         ua->ErrorMsg(_("Storage name missing.\n"));
1610         return true;
1611       }
1612     }
1613   }
1614 
1615   if (client) { DoClientResolve(ua, client); }
1616 
1617   if (storage) { DoStorageResolve(ua, storage); }
1618 
1619   if (!client && !storage) {
1620     dlist* addr_list;
1621     const char* errstr;
1622     char addresses[2048];
1623 
1624     for (int i = 1; i < ua->argc; i++) {
1625       if (!*ua->argk[i]) { continue; }
1626 
1627       if ((addr_list = BnetHost2IpAddrs(ua->argk[i], 0, &errstr)) == NULL) {
1628         ua->ErrorMsg(_("%s Failed to resolve %s\n"), my_name, ua->argk[i]);
1629         return false;
1630       }
1631       ua->SendMsg(
1632           _("%s resolves %s to %s\n"), my_name, ua->argk[i],
1633           BuildAddressesString(addr_list, addresses, sizeof(addresses), false));
1634       FreeAddresses(addr_list);
1635     }
1636   }
1637 
1638   return true;
1639 }
1640 
1641 /**
1642  * Turn debug tracing to file on/off
1643  */
TraceCmd(UaContext * ua,const char * cmd)1644 static bool TraceCmd(UaContext* ua, const char* cmd)
1645 {
1646   char* onoff;
1647 
1648   if (ua->argc != 2) {
1649     if (!GetCmd(ua, _("Turn on or off? "))) { return true; }
1650     onoff = ua->cmd;
1651   } else {
1652     onoff = ua->argk[1];
1653   }
1654 
1655 
1656   SetTrace((Bstrcasecmp(onoff, NT_("off"))) ? false : true);
1657   return true;
1658 }
1659 
var_cmd(UaContext * ua,const char * cmd)1660 static bool var_cmd(UaContext* ua, const char* cmd)
1661 {
1662   if (!OpenClientDb(ua)) { return true; }
1663 
1664   char* var;
1665   for (var = ua->cmd; *var != ' ';) { /* skip command */
1666     var++;
1667   }
1668   while (*var == ' ') { /* skip spaces */
1669     var++;
1670   }
1671   Dmsg1(100, "Var=%s:\n", var);
1672   POOLMEM* val = GetPoolMemory(PM_FNAME);
1673   VariableExpansion(ua->jcr, var, val);
1674   ua->SendMsg("%s\n", val);
1675   FreePoolMemory(val);
1676   return true;
1677 }
1678 
EstimateCmd(UaContext * ua,const char * cmd)1679 static bool EstimateCmd(UaContext* ua, const char* cmd)
1680 {
1681   JobResource* job = NULL;
1682   ClientResource* client = NULL;
1683   FilesetResource* fileset = NULL;
1684   int listing = 0;
1685   JobControlRecord* jcr = ua->jcr;
1686   bool accurate_set = false;
1687   bool accurate = false;
1688 
1689   jcr->setJobLevel(L_FULL);
1690   for (int i = 1; i < ua->argc; i++) {
1691     if (Bstrcasecmp(ua->argk[i], NT_("client")) ||
1692         Bstrcasecmp(ua->argk[i], NT_("fd"))) {
1693       if (ua->argv[i]) {
1694         client = ua->GetClientResWithName(ua->argv[i]);
1695         if (!client) {
1696           ua->ErrorMsg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1697           return false;
1698         }
1699         continue;
1700       } else {
1701         ua->ErrorMsg(_("Client name missing.\n"));
1702         return false;
1703       }
1704     }
1705 
1706     if (Bstrcasecmp(ua->argk[i], NT_("job"))) {
1707       if (ua->argv[i]) {
1708         job = ua->GetJobResWithName(ua->argv[i]);
1709         if (!job) {
1710           ua->ErrorMsg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1711           return false;
1712         }
1713         continue;
1714       } else {
1715         ua->ErrorMsg(_("Job name missing.\n"));
1716         return false;
1717       }
1718     }
1719 
1720     if (Bstrcasecmp(ua->argk[i], NT_("fileset"))) {
1721       if (ua->argv[i]) {
1722         fileset = ua->GetFileSetResWithName(ua->argv[i]);
1723         if (!fileset) {
1724           ua->ErrorMsg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1725           return false;
1726         }
1727         continue;
1728       } else {
1729         ua->ErrorMsg(_("Fileset name missing.\n"));
1730         return false;
1731       }
1732     }
1733 
1734     if (Bstrcasecmp(ua->argk[i], NT_("listing"))) {
1735       listing = 1;
1736       continue;
1737     }
1738 
1739     if (Bstrcasecmp(ua->argk[i], NT_("level"))) {
1740       if (ua->argv[i]) {
1741         if (!GetLevelFromName(jcr, ua->argv[i])) {
1742           ua->ErrorMsg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1743         }
1744         continue;
1745       } else {
1746         ua->ErrorMsg(_("Level value missing.\n"));
1747         return false;
1748       }
1749     }
1750 
1751     if (Bstrcasecmp(ua->argk[i], NT_("accurate"))) {
1752       if (ua->argv[i]) {
1753         if (!IsYesno(ua->argv[i], &accurate)) {
1754           ua->ErrorMsg(
1755               _("Invalid value for accurate. "
1756                 "It must be yes or no.\n"));
1757         }
1758         accurate_set = true;
1759         continue;
1760       } else {
1761         ua->ErrorMsg(_("Accurate value missing.\n"));
1762         return false;
1763       }
1764     }
1765   }
1766 
1767   if (!job && !(client && fileset)) {
1768     if (!(job = select_job_resource(ua))) { return false; }
1769   }
1770 
1771   if (!job) {
1772     job = ua->GetJobResWithName(ua->argk[1]);
1773     if (!job) {
1774       ua->ErrorMsg(_("No job specified.\n"));
1775       return false;
1776     }
1777   }
1778 
1779   switch (job->JobType) {
1780     case JT_BACKUP:
1781       break;
1782     default:
1783       ua->ErrorMsg(_("Wrong job specified of type %s.\n"),
1784                    job_type_to_str(job->JobType));
1785       return false;
1786   }
1787 
1788   if (!client) { client = job->client; }
1789 
1790   if (!fileset) { fileset = job->fileset; }
1791 
1792   if (!client) {
1793     ua->ErrorMsg(_("No client specified or selected.\n"));
1794     return false;
1795   }
1796 
1797   if (!fileset) {
1798     ua->ErrorMsg(_("No fileset specified or selected.\n"));
1799     return false;
1800   }
1801 
1802   jcr->impl->res.client = client;
1803   jcr->impl->res.fileset = fileset;
1804   CloseDb(ua);
1805 
1806   switch (client->Protocol) {
1807     case APT_NATIVE:
1808       break;
1809     default:
1810       ua->ErrorMsg(_("Estimate is only supported on native clients.\n"));
1811       return false;
1812   }
1813 
1814   if (job->pool->catalog) {
1815     ua->catalog = job->pool->catalog;
1816   } else {
1817     ua->catalog = client->catalog;
1818   }
1819 
1820   if (!OpenDb(ua)) { return false; }
1821 
1822   jcr->impl->res.job = job;
1823   jcr->setJobType(JT_BACKUP);
1824   jcr->start_time = time(NULL);
1825   InitJcrJobRecord(jcr);
1826 
1827   if (!GetOrCreateClientRecord(jcr)) { return true; }
1828 
1829   if (!GetOrCreateFilesetRecord(jcr)) { return true; }
1830 
1831   GetLevelSinceTime(jcr);
1832 
1833   ua->SendMsg(_("Connecting to Client %s at %s:%d\n"),
1834               jcr->impl->res.client->resource_name_,
1835               jcr->impl->res.client->address, jcr->impl->res.client->FDport);
1836   if (!ConnectToFileDaemon(jcr, 1, 15, false)) {
1837     ua->ErrorMsg(_("Failed to connect to Client.\n"));
1838     return false;
1839   }
1840 
1841   if (!SendJobInfoToFileDaemon(jcr)) {
1842     ua->ErrorMsg(_("Failed to connect to Client.\n"));
1843     return false;
1844   }
1845 
1846   /*
1847    * The level string change if accurate mode is enabled
1848    */
1849   if (accurate_set) {
1850     jcr->accurate = accurate;
1851   } else {
1852     jcr->accurate = job->accurate;
1853   }
1854 
1855   if (!SendLevelCommand(jcr)) { goto bail_out; }
1856 
1857   if (!SendIncludeList(jcr)) {
1858     ua->ErrorMsg(_("Error sending include list.\n"));
1859     goto bail_out;
1860   }
1861 
1862   if (!SendExcludeList(jcr)) {
1863     ua->ErrorMsg(_("Error sending exclude list.\n"));
1864     goto bail_out;
1865   }
1866 
1867   /*
1868    * If the job is in accurate mode, we send the list of all files to FD.
1869    */
1870   Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1871   if (!SendAccurateCurrentFiles(jcr)) { goto bail_out; }
1872 
1873   jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1874   while (jcr->file_bsock->recv() >= 0) {
1875     ua->SendMsg("%s", jcr->file_bsock->msg);
1876   }
1877 
1878 bail_out:
1879   if (jcr->file_bsock) {
1880     jcr->file_bsock->signal(BNET_TERMINATE);
1881     jcr->file_bsock->close();
1882     delete jcr->file_bsock;
1883     jcr->file_bsock = NULL;
1884   }
1885   return true;
1886 }
1887 
1888 /**
1889  * Print time
1890  */
time_cmd(UaContext * ua,const char * cmd)1891 static bool time_cmd(UaContext* ua, const char* cmd)
1892 {
1893   char sdt[50];
1894   time_t ttime = time(NULL);
1895 
1896   ua->send->ObjectStart("time");
1897 
1898   bstrftime(sdt, sizeof(sdt), ttime, "%a %d-%b-%Y %H:%M:%S");
1899   ua->send->ObjectKeyValue("full", sdt, "%s\n");
1900 
1901   bstrftime(sdt, sizeof(sdt), ttime, "%Y");
1902   ua->send->ObjectKeyValue("year", sdt);
1903   bstrftime(sdt, sizeof(sdt), ttime, "%m");
1904   ua->send->ObjectKeyValue("month", sdt);
1905   bstrftime(sdt, sizeof(sdt), ttime, "%d");
1906   ua->send->ObjectKeyValue("day", sdt);
1907   bstrftime(sdt, sizeof(sdt), ttime, "%H");
1908   ua->send->ObjectKeyValue("hour", sdt);
1909   bstrftime(sdt, sizeof(sdt), ttime, "%M");
1910   ua->send->ObjectKeyValue("minute", sdt);
1911   bstrftime(sdt, sizeof(sdt), ttime, "%S");
1912   ua->send->ObjectKeyValue("second", sdt);
1913 
1914   ua->send->ObjectEnd("time");
1915 
1916   return true;
1917 }
1918 
1919 
1920 /**
1921  * truncate command. Truncates volumes (volume files) on the storage daemon.
1922  *
1923  * usage:
1924  * truncate volstatus=Purged [storage=<storage>] [pool=<pool>]
1925  * [volume=<volume>] [yes]
1926  */
TruncateCmd(UaContext * ua,const char * cmd)1927 static bool TruncateCmd(UaContext* ua, const char* cmd)
1928 {
1929   bool result = false;
1930   int i = -1;
1931   int parsed_args = 1; /* start at 1, as command itself is also counted */
1932   char esc[MAX_NAME_LENGTH * 2 + 1];
1933   PoolMem tmp(PM_MESSAGE);
1934   PoolMem volumes(PM_MESSAGE);
1935   dbid_list mediaIds;
1936   MediaDbRecord mr;
1937   PoolDbRecord pool_dbr;
1938   StorageDbRecord storage_dbr;
1939 
1940   /*
1941    * Look for volumes that can be recycled,
1942    * are enabled and have used more than the first block.
1943    */
1944   mr.Recycle = 1;
1945   mr.Enabled = VOL_ENABLED;
1946   mr.VolBytes = (512 * 126); /* search volumes with more than 64,512 bytes
1947                                 (DEFAULT_BLOCK_SIZE) */
1948 
1949   if (!(ua->argc > 1)) {
1950     ua->SendCmdUsage(_("missing parameter"));
1951     return false;
1952   }
1953 
1954   /* arg: volstatus=Purged */
1955   i = FindArgWithValue(ua, "volstatus");
1956   if (i < 0) {
1957     ua->SendCmdUsage(_("required parameter 'volstatus' missing"));
1958     return false;
1959   }
1960   if (!(Bstrcasecmp(ua->argv[i], "Purged"))) {
1961     ua->SendCmdUsage(_("Invalid parameter. 'volstatus' must be 'Purged'."));
1962     return false;
1963   }
1964   parsed_args++;
1965 
1966   /*
1967    * Look for Purged volumes.
1968    */
1969   bstrncpy(mr.VolStatus, "Purged", sizeof(mr.VolStatus));
1970 
1971   if (!OpenDb(ua)) {
1972     ua->ErrorMsg("Failed to open db.\n");
1973     goto bail_out;
1974   }
1975 
1976   ua->send->AddAclFilterTuple(2, Pool_ACL);
1977   ua->send->AddAclFilterTuple(3, Storage_ACL);
1978 
1979   /*
1980    * storage parameter is only required
1981    * if ACL forbids access to all storages.
1982    * Otherwise the user should not be asked for this parameter.
1983    */
1984   i = FindArgWithValue(ua, "storage");
1985   if ((i >= 0) || ua->AclHasRestrictions(Storage_ACL)) {
1986     if (!SelectStorageDbr(ua, &storage_dbr, "storage")) { goto bail_out; }
1987     mr.StorageId = storage_dbr.StorageId;
1988     parsed_args++;
1989   }
1990 
1991   /*
1992    * pool parameter is only required
1993    * if ACL forbids access to all pools.
1994    * Otherwise the user should not be asked for this parameter.
1995    */
1996   i = FindArgWithValue(ua, "pool");
1997   if ((i >= 0) || ua->AclHasRestrictions(Pool_ACL)) {
1998     if (!SelectPoolDbr(ua, &pool_dbr, "pool")) { goto bail_out; }
1999     mr.PoolId = pool_dbr.PoolId;
2000     if (i >= 0) { parsed_args++; }
2001   }
2002 
2003   /*
2004    * parse volume parameter.
2005    * Currently only support one volume parameter
2006    * (multiple volume parameter have been intended before,
2007    * but this causes problems with parsing and ACL handling).
2008    */
2009   i = FindArgWithValue(ua, "volume");
2010   if (i >= 0) {
2011     if (IsNameValid(ua->argv[i])) {
2012       ua->db->EscapeString(ua->jcr, esc, ua->argv[i], strlen(ua->argv[i]));
2013       if (!*volumes.c_str()) {
2014         Mmsg(tmp, "'%s'", esc);
2015       } else {
2016         Mmsg(tmp, ",'%s'", esc);
2017       }
2018       volumes.strcat(tmp.c_str());
2019       parsed_args++;
2020     }
2021   }
2022 
2023   if (FindArg(ua, NT_("yes")) >= 0) {
2024     /* parameter yes is evaluated at 'GetConfirmation' */
2025     parsed_args++;
2026   }
2027 
2028   if (parsed_args != ua->argc) {
2029     ua->SendCmdUsage(_("Invalid parameter."));
2030     goto bail_out;
2031   }
2032 
2033   /* create sql query string (in ua->db->cmd) */
2034   if (!ua->db->PrepareMediaSqlQuery(ua->jcr, &mr, &tmp, volumes)) {
2035     ua->ErrorMsg(_("Invalid parameter (failed to create sql query).\n"));
2036     goto bail_out;
2037   }
2038 
2039   /* execute query and display result */
2040   ua->db->ListSqlQuery(ua->jcr, tmp.c_str(), ua->send, HORZ_LIST, "volumes",
2041                        true);
2042 
2043   /*
2044    * execute query to get media ids.
2045    * Second execution is only required,
2046    * because function is also used in other contextes.
2047    */
2048   // tmp.strcpy(ua->db->cmd);
2049   if (!ua->db->GetQueryDbids(ua->jcr, tmp, mediaIds)) {
2050     Dmsg0(100, "No results from db_get_query_dbids\n");
2051     goto bail_out;
2052   }
2053 
2054   if (!mediaIds.size()) {
2055     Dmsg0(100, "Results are empty\n");
2056     goto bail_out;
2057   }
2058 
2059   if (!ua->db->VerifyMediaIdsFromSingleStorage(ua->jcr, mediaIds)) {
2060     ua->ErrorMsg(
2061         "Selected volumes are from different storages. "
2062         "This is not supported. Please choose only volumes from a single "
2063         "storage.\n");
2064     goto bail_out;
2065   }
2066 
2067   mr.MediaId = mediaIds.get(0);
2068   if (!ua->db->GetMediaRecord(ua->jcr, &mr)) { goto bail_out; }
2069 
2070   if (ua->GetStoreResWithId(mr.StorageId)->Protocol != APT_NATIVE) {
2071     ua->WarningMsg(
2072         _("Storage uses a non-native protocol. Truncate is only supported for "
2073           "native protocols.\n"));
2074     goto bail_out;
2075   }
2076 
2077   if (!GetConfirmation(ua, _("Truncate listed volumes (yes/no)? "))) {
2078     goto bail_out;
2079   }
2080 
2081   /*
2082    * Loop over the candidate Volumes and actually truncate them
2083    */
2084   for (int i = 0; i < mediaIds.size(); i++) {
2085     mr.MediaId = mediaIds.get(i);
2086     if (!ua->db->GetMediaRecord(ua->jcr, &mr)) {
2087       Dmsg1(0, "Can't find MediaId=%lld\n", (uint64_t)mr.MediaId);
2088     } else {
2089       DoTruncate(ua, mr);
2090     }
2091   }
2092 
2093 bail_out:
2094   CloseDb(ua);
2095   if (ua->jcr->store_bsock) {
2096     ua->jcr->store_bsock->signal(BNET_TERMINATE);
2097     ua->jcr->store_bsock->close();
2098     delete ua->jcr->store_bsock;
2099     ua->jcr->store_bsock = NULL;
2100   }
2101   return result;
2102 }
2103 
DoTruncate(UaContext * ua,MediaDbRecord & mr)2104 static bool DoTruncate(UaContext* ua, MediaDbRecord& mr)
2105 {
2106   bool retval = false;
2107   StorageDbRecord storage_dbr;
2108   PoolDbRecord pool_dbr;
2109 
2110 
2111   storage_dbr.StorageId = mr.StorageId;
2112   if (!ua->db->GetStorageRecord(ua->jcr, &storage_dbr)) {
2113     ua->ErrorMsg("failed to determine storage for id %lld\n", mr.StorageId);
2114     goto bail_out;
2115   }
2116 
2117   pool_dbr.PoolId = mr.PoolId;
2118   if (!ua->db->GetPoolRecord(ua->jcr, &pool_dbr)) {
2119     ua->ErrorMsg("failed to determine pool for id %lld\n", mr.PoolId);
2120     goto bail_out;
2121   }
2122 
2123   /*
2124    * Choose storage
2125    */
2126   ua->jcr->impl->res.write_storage = ua->GetStoreResWithName(storage_dbr.Name);
2127   if (!ua->jcr->impl->res.write_storage) {
2128     ua->ErrorMsg("failed to determine storage resource by name %s\n",
2129                  storage_dbr.Name);
2130     goto bail_out;
2131   }
2132 
2133   if (SendLabelRequest(ua, ua->jcr->impl->res.write_storage, &mr, &mr,
2134                        &pool_dbr,
2135                        /* bool media_record_exists */
2136                        true,
2137                        /* bool relabel */
2138                        true,
2139                        /* drive_number_t drive */
2140                        0,
2141                        /* slot_number_t slot */
2142                        0)) {
2143     ua->SendMsg(_("The volume '%s' has been truncated.\n"), mr.VolumeName);
2144     retval = true;
2145   }
2146 
2147 bail_out:
2148   ua->jcr->impl->res.write_storage = NULL;
2149   return retval;
2150 }
2151 
2152 /**
2153  * Reload the conf file
2154  */
ReloadCmd(UaContext * ua,const char * cmd)2155 static bool ReloadCmd(UaContext* ua, const char* cmd)
2156 {
2157   bool result;
2158 
2159   result = DoReloadConfig();
2160 
2161   ua->send->ObjectStart("reload");
2162   if (result) {
2163     ua->send->ObjectKeyValueBool("success", result, "reloaded\n");
2164   } else {
2165     ua->send->ObjectKeyValueBool("success", result, "failed to reload\n");
2166   }
2167   ua->send->ObjectEnd("reload");
2168 
2169   return result;
2170 }
2171 
2172 /**
2173  * Delete Pool records (should purge Media with it).
2174  *
2175  * delete pool=<pool-name>
2176  * delete volume pool=<pool-name> volume=<name>
2177  * delete jobid=<jobid>
2178  */
DeleteCmd(UaContext * ua,const char * cmd)2179 static bool DeleteCmd(UaContext* ua, const char* cmd)
2180 {
2181   static const char* keywords[] = {NT_("volume"), NT_("pool"), NT_("jobid"),
2182                                    NULL};
2183 
2184   if (!OpenClientDb(ua, true)) { return true; }
2185 
2186   switch (FindArgKeyword(ua, keywords)) {
2187     case 0:
2188       DeleteVolume(ua);
2189       return true;
2190     case 1:
2191       DeletePool(ua);
2192       return true;
2193     case 2:
2194       int i;
2195       while ((i = FindArg(ua, "jobid")) > 0) {
2196         DeleteJob(ua);
2197         *ua->argk[i] = 0; /* zap keyword already visited */
2198       }
2199       return true;
2200     default:
2201       break;
2202   }
2203 
2204   ua->WarningMsg(
2205       _("In general it is not a good idea to delete either a\n"
2206         "Pool or a Volume since they may contain data.\n\n"));
2207 
2208   switch (DoKeywordPrompt(ua, _("Choose catalog item to delete"), keywords)) {
2209     case 0:
2210       DeleteVolume(ua);
2211       break;
2212     case 1:
2213       DeletePool(ua);
2214       break;
2215     case 2:
2216       DeleteJob(ua);
2217       return true;
2218     default:
2219       ua->WarningMsg(_("Nothing done.\n"));
2220       break;
2221   }
2222   return true;
2223 }
2224 
2225 /**
2226  * DeleteJob has been modified to parse JobID lists like the following:
2227  * delete JobID=3,4,6,7-11,14
2228  *
2229  * Thanks to Phil Stracchino for the above addition.
2230  */
DeleteJob(UaContext * ua)2231 static void DeleteJob(UaContext* ua)
2232 {
2233   int i;
2234   JobId_t JobId;
2235   char *s, *sep, *tok;
2236 
2237   i = FindArgWithValue(ua, NT_("jobid"));
2238   if (i >= 0) {
2239     if (strchr(ua->argv[i], ',') || strchr(ua->argv[i], '-')) {
2240       s = strdup(ua->argv[i]);
2241       tok = s;
2242 
2243       /*
2244        * We could use strtok() here.  But we're not going to, because:
2245        * (a) strtok() is deprecated, having been replaced by strsep();
2246        * (b) strtok() is broken in significant ways.
2247        * we could use strsep() instead, but it's not universally available.
2248        * so we grow our own using strchr().
2249        */
2250       sep = strchr(tok, ',');
2251       while (sep != NULL) {
2252         *sep = '\0';
2253         if (!DeleteJobIdRange(ua, tok)) {
2254           if (Is_a_number(tok)) {
2255             JobId = (JobId_t)str_to_uint64(tok);
2256             DoJobDelete(ua, JobId);
2257           } else {
2258             ua->WarningMsg(_("Illegal JobId %s ignored\n"), tok);
2259           }
2260         }
2261         tok = ++sep;
2262         sep = strchr(tok, ',');
2263       }
2264 
2265       /*
2266        * Pick up the last token
2267        */
2268       if (!DeleteJobIdRange(ua, tok)) {
2269         if (Is_a_number(tok)) {
2270           JobId = (JobId_t)str_to_uint64(tok);
2271           DoJobDelete(ua, JobId);
2272         } else {
2273           ua->WarningMsg(_("Illegal JobId %s ignored\n"), tok);
2274         }
2275       }
2276 
2277       free(s);
2278     } else {
2279       if (Is_a_number(ua->argv[i])) {
2280         JobId = (JobId_t)str_to_uint64(ua->argv[i]);
2281         DoJobDelete(ua, JobId);
2282       } else {
2283         ua->WarningMsg(_("Illegal JobId %s ignored\n"), ua->argv[i]);
2284       }
2285     }
2286   } else if (!GetPint(ua, _("Enter JobId to delete: "))) {
2287     return;
2288   } else {
2289     JobId = ua->int64_val;
2290     DoJobDelete(ua, JobId);
2291   }
2292 }
2293 
2294 /**
2295  * We call DeleteJobIdRange to parse range tokens and iterate over ranges
2296  */
DeleteJobIdRange(UaContext * ua,char * tok)2297 static bool DeleteJobIdRange(UaContext* ua, char* tok)
2298 {
2299   char buf[64];
2300   char* tok2;
2301   JobId_t j, j1, j2;
2302 
2303   tok2 = strchr(tok, '-');
2304   if (!tok2) { return false; }
2305 
2306   *tok2 = '\0';
2307   tok2++;
2308 
2309   if (Is_a_number(tok) && Is_a_number(tok2)) {
2310     j1 = (JobId_t)str_to_uint64(tok);
2311     j2 = (JobId_t)str_to_uint64(tok2);
2312 
2313     if (j2 > j1) {
2314       /*
2315        * See if the range is big if more then 25 Jobs are deleted
2316        * ask the user for confirmation.
2317        */
2318       if ((j2 - j1) > 25) {
2319         Bsnprintf(buf, sizeof(buf),
2320                   _("Are you sure you want to delete %d JobIds ? (yes/no): "),
2321                   j2 - j1);
2322         if (!GetYesno(ua, buf) || !ua->pint32_val) { return true; }
2323       }
2324       for (j = j1; j <= j2; j++) { DoJobDelete(ua, j); }
2325     } else {
2326       ua->WarningMsg(_("Illegal JobId range %s - %s should define increasing "
2327                        "JobIds, ignored\n"),
2328                      tok, tok2);
2329     }
2330   } else {
2331     ua->WarningMsg(_("Illegal JobId range %s - %s, ignored\n"), tok, tok2);
2332   }
2333 
2334   return true;
2335 }
2336 
2337 /**
2338  * DoJobDelete now performs the actual delete operation atomically
2339  */
DoJobDelete(UaContext * ua,JobId_t JobId)2340 static void DoJobDelete(UaContext* ua, JobId_t JobId)
2341 {
2342   char ed1[50];
2343 
2344   edit_int64(JobId, ed1);
2345   PurgeJobsFromCatalog(ua, ed1);
2346   ua->SendMsg(_("Jobid %s and associated records deleted from the catalog.\n"),
2347               ed1);
2348 }
2349 
2350 /**
2351  * Delete media records from database -- dangerous
2352  */
DeleteVolume(UaContext * ua)2353 static bool DeleteVolume(UaContext* ua)
2354 {
2355   MediaDbRecord mr;
2356   char buf[1000];
2357   db_list_ctx lst;
2358 
2359   if (!SelectMediaDbr(ua, &mr)) { return true; }
2360   ua->WarningMsg(_("\nThis command will delete volume %s\n"
2361                    "and all Jobs saved on that volume from the Catalog\n"),
2362                  mr.VolumeName);
2363 
2364   if (FindArg(ua, "yes") >= 0) {
2365     ua->pint32_val = 1; /* Have "yes" on command line already" */
2366   } else {
2367     Bsnprintf(buf, sizeof(buf),
2368               _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
2369               mr.VolumeName);
2370     if (!GetYesno(ua, buf)) { return true; }
2371   }
2372   if (!ua->pint32_val) { return true; }
2373 
2374   /*
2375    * If not purged, do it
2376    */
2377   if (!bstrcmp(mr.VolStatus, "Purged")) {
2378     if (!ua->db->GetVolumeJobids(ua->jcr, &mr, &lst)) {
2379       ua->ErrorMsg(_("Can't list jobs on this volume\n"));
2380       return true;
2381     }
2382     if (lst.count) { PurgeJobsFromCatalog(ua, lst.list); }
2383   }
2384 
2385   ua->db->DeleteMediaRecord(ua->jcr, &mr);
2386   return true;
2387 }
2388 
2389 /**
2390  * Delete a pool record from the database -- dangerous
2391  */
DeletePool(UaContext * ua)2392 static bool DeletePool(UaContext* ua)
2393 {
2394   PoolDbRecord pr;
2395   char buf[200];
2396 
2397 
2398   if (!GetPoolDbr(ua, &pr)) { return true; }
2399   Bsnprintf(buf, sizeof(buf),
2400             _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
2401             pr.Name);
2402   if (!GetYesno(ua, buf)) { return true; }
2403   if (ua->pint32_val) { ua->db->DeletePoolRecord(ua->jcr, &pr); }
2404   return true;
2405 }
2406 
MemoryCmd(UaContext * ua,const char * cmd)2407 static bool MemoryCmd(UaContext* ua, const char* cmd)
2408 {
2409   GarbageCollectMemory();
2410   ListDirStatusHeader(ua);
2411   return true;
2412 }
2413 
DoMountCmd(UaContext * ua,const char * cmd)2414 static void DoMountCmd(UaContext* ua, const char* cmd)
2415 {
2416   UnifiedStorageResource store;
2417   drive_number_t nr_drives;
2418   drive_number_t drive = kInvalidDriveNumber;
2419   slot_number_t slot = kInvalidSlotNumber;
2420   bool do_alldrives = false;
2421 
2422   if ((bstrcmp(cmd, "release") || bstrcmp(cmd, "unmount")) &&
2423       FindArg(ua, "alldrives") >= 0) {
2424     do_alldrives = true;
2425   }
2426 
2427   if (!OpenClientDb(ua)) { return; }
2428   Dmsg2(120, "%s: %s\n", cmd, ua->UA_sock->msg);
2429 
2430   store.store = get_storage_resource(ua, true, false);
2431   if (!store.store) { return; }
2432 
2433   PmStrcpy(store.store_source, _("unknown source"));
2434   SetWstorage(ua->jcr, &store);
2435   if (!do_alldrives) {
2436     drive = GetStorageDrive(ua, store.store);
2437     if (drive == kInvalidDriveNumber) { return; }
2438   }
2439   if (bstrcmp(cmd, "mount")) { slot = GetStorageSlot(ua, store.store); }
2440 
2441   Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%hd\n",
2442         store.store->media_type, store.store->dev_name(), drive);
2443 
2444   if (!do_alldrives) {
2445     DoAutochangerVolumeOperation(ua, store.store, cmd, drive, slot);
2446   } else {
2447     nr_drives = GetNumDrives(ua, ua->jcr->impl->res.write_storage);
2448     for (drive_number_t i = 0; i < nr_drives; i++) {
2449       DoAutochangerVolumeOperation(ua, store.store, cmd, i, slot);
2450     }
2451   }
2452 
2453   InvalidateVolList(store.store);
2454 }
2455 
2456 /**
2457  * mount [storage=<name>] [drive=nn] [slot=mm]
2458  */
MountCmd(UaContext * ua,const char * cmd)2459 static bool MountCmd(UaContext* ua, const char* cmd)
2460 {
2461   DoMountCmd(ua, "mount"); /* mount */
2462   return true;
2463 }
2464 
2465 /**
2466  * unmount [storage=<name>] [drive=nn]
2467  */
UnmountCmd(UaContext * ua,const char * cmd)2468 static bool UnmountCmd(UaContext* ua, const char* cmd)
2469 {
2470   DoMountCmd(ua, "unmount"); /* unmount */
2471   return true;
2472 }
2473 
2474 /**
2475  * Perform a NO-OP.
2476  */
noop_cmd(UaContext * ua,const char * cmd)2477 static bool noop_cmd(UaContext* ua, const char* cmd)
2478 {
2479   if (ua->api) { ua->signal(BNET_CMD_BEGIN); }
2480 
2481   if (ua->api) { ua->signal(BNET_CMD_OK); }
2482 
2483   return true; /* no op */
2484 }
2485 
2486 /**
2487  * release [storage=<name>] [drive=nn]
2488  */
ReleaseCmd(UaContext * ua,const char * cmd)2489 static bool ReleaseCmd(UaContext* ua, const char* cmd)
2490 {
2491   DoMountCmd(ua, "release"); /* release */
2492   return true;
2493 }
2494 
2495 /**
2496  * Switch databases
2497  * use catalog=<name>
2498  */
use_cmd(UaContext * ua,const char * cmd)2499 static bool use_cmd(UaContext* ua, const char* cmd)
2500 {
2501   CatalogResource *oldcatalog, *catalog;
2502 
2503   CloseDb(ua); /* close any previously open db */
2504   oldcatalog = ua->catalog;
2505 
2506   if (!(catalog = get_catalog_resource(ua))) {
2507     ua->catalog = oldcatalog;
2508   } else {
2509     ua->catalog = catalog;
2510   }
2511   if (OpenDb(ua)) {
2512     ua->SendMsg(_("Using Catalog name=%s DB=%s\n"), ua->catalog->resource_name_,
2513                 ua->catalog->db_name);
2514   }
2515   return true;
2516 }
2517 
quit_cmd(UaContext * ua,const char * cmd)2518 bool quit_cmd(UaContext* ua, const char* cmd)
2519 {
2520   ua->quit = true;
2521 
2522   return true;
2523 }
2524 
2525 /**
2526  * Handler to get job status
2527  */
StatusHandler(void * ctx,int num_fields,char ** row)2528 static int StatusHandler(void* ctx, int num_fields, char** row)
2529 {
2530   char* val = (char*)ctx;
2531 
2532   if (row[0]) {
2533     *val = row[0][0];
2534   } else {
2535     *val = '?'; /* Unknown by default */
2536   }
2537 
2538   return 0;
2539 }
2540 
2541 /**
2542  * Wait until no job is running
2543  */
wait_cmd(UaContext * ua,const char * cmd)2544 static bool wait_cmd(UaContext* ua, const char* cmd)
2545 {
2546   int i;
2547   JobControlRecord* jcr;
2548   int status;
2549   char ed1[50];
2550   uint32_t JobId = 0;
2551   time_t stop_time = 0;
2552   char jobstatus = '?'; /* Unknown by default */
2553   PoolMem temp(PM_MESSAGE);
2554 
2555   /*
2556    * no args
2557    * Wait until no job is running
2558    */
2559   if (ua->argc == 1) {
2560     Bmicrosleep(0, 200000); /* let job actually start */
2561     for (bool running = true; running;) {
2562       running = false;
2563       foreach_jcr (jcr) {
2564         if (jcr->JobId != 0) {
2565           running = true;
2566           break;
2567         }
2568       }
2569       endeach_jcr(jcr);
2570 
2571       if (running) { Bmicrosleep(1, 0); }
2572     }
2573     return true;
2574   }
2575 
2576   i = FindArgWithValue(ua, NT_("timeout"));
2577   if (i > 0 && ua->argv[i]) {
2578     stop_time = time(NULL) + str_to_int64(ua->argv[i]);
2579   }
2580 
2581   /*
2582    * We have jobid, jobname or ujobid argument
2583    */
2584   if (!OpenClientDb(ua)) {
2585     ua->ErrorMsg(_("ERR: Can't open db\n"));
2586     return true;
2587   }
2588 
2589   for (int i = 1; i < ua->argc; i++) {
2590     if (Bstrcasecmp(ua->argk[i], "jobid")) {
2591       if (!ua->argv[i]) { break; }
2592       JobId = str_to_int64(ua->argv[i]);
2593       break;
2594     } else if (Bstrcasecmp(ua->argk[i], "jobname") ||
2595                Bstrcasecmp(ua->argk[i], "job")) {
2596       if (!ua->argv[i]) { break; }
2597       jcr = get_jcr_by_partial_name(ua->argv[i]);
2598       if (jcr) {
2599         JobId = jcr->JobId;
2600         FreeJcr(jcr);
2601       }
2602       break;
2603     } else if (Bstrcasecmp(ua->argk[i], "ujobid")) {
2604       if (!ua->argv[i]) { break; }
2605       jcr = get_jcr_by_full_name(ua->argv[i]);
2606       if (jcr) {
2607         JobId = jcr->JobId;
2608         FreeJcr(jcr);
2609       }
2610       break;
2611     } else if (Bstrcasecmp(ua->argk[i], "mount")) {
2612       /*
2613        * Wait for a mount request
2614        */
2615       for (bool waiting = false; !waiting;) {
2616         foreach_jcr (jcr) {
2617           if (jcr->JobId != 0 && (jcr->JobStatus == JS_WaitMedia ||
2618                                   jcr->JobStatus == JS_WaitMount)) {
2619             waiting = true;
2620             break;
2621           }
2622         }
2623         endeach_jcr(jcr);
2624         if (waiting) { break; }
2625         if (stop_time && (time(NULL) >= stop_time)) {
2626           ua->WarningMsg(_("Wait on mount timed out\n"));
2627           return true;
2628         }
2629         Bmicrosleep(1, 0);
2630       }
2631       return true;
2632     }
2633   }
2634 
2635   if (JobId == 0) {
2636     ua->ErrorMsg(_("ERR: Job was not found\n"));
2637     return true;
2638   }
2639 
2640   /*
2641    * We wait the end of a specific job
2642    */
2643   Bmicrosleep(0, 200000); /* let job actually start */
2644   for (bool running = true; running;) {
2645     running = false;
2646 
2647     jcr = get_jcr_by_id(JobId);
2648     if (jcr) {
2649       running = true;
2650       FreeJcr(jcr);
2651     }
2652 
2653     if (running) { Bmicrosleep(1, 0); }
2654   }
2655 
2656   /*
2657    * We have to get JobStatus
2658    */
2659   Mmsg(temp, "SELECT JobStatus FROM Job WHERE JobId='%s'",
2660        edit_int64(JobId, ed1));
2661   ua->db->SqlQuery(temp.c_str(), StatusHandler, (void*)&jobstatus);
2662 
2663   switch (jobstatus) {
2664     case JS_Error:
2665       status = 1; /* Warning */
2666       break;
2667     case JS_FatalError:
2668     case JS_ErrorTerminated:
2669     case JS_Canceled:
2670       status = 2; /* Critical */
2671       break;
2672     case JS_Warnings:
2673     case JS_Terminated:
2674       status = 0; /* Ok */
2675       break;
2676     default:
2677       status = 3; /* Unknown */
2678       break;
2679   }
2680 
2681   Mmsg(temp, "%c", jobstatus);
2682   ua->send->ObjectStart("Job");
2683   ua->send->ObjectKeyValue("JobId", "%s=", JobId, "%i\n");
2684   ua->send->ObjectKeyValue("JobStatusLong", job_status_to_str(jobstatus),
2685                            "JobStatus=%s ");
2686   ua->send->ObjectKeyValue("JobStatus", temp.c_str(), "(%s)\n");
2687   ua->send->ObjectKeyValue("ExitStatus", status);
2688   ua->send->ObjectEnd("Job");
2689 
2690   return true;
2691 }
2692 
WhoAmICmd(UaContext * ua,const char * cmd)2693 static bool WhoAmICmd(UaContext* ua, const char* cmd)
2694 {
2695   std::string message;
2696   message = ua->user_acl ? ua->user_acl->corresponding_resource->resource_name_
2697                          : "root";
2698   ua->send->ObjectKeyValue("whoami", message.c_str(), "%s\n");
2699   return true;
2700 }
2701 
help_cmd(UaContext * ua,const char * cmd)2702 static bool help_cmd(UaContext* ua, const char* cmd)
2703 {
2704   int i;
2705 
2706   ua->send->Decoration("%s", _("  Command            Description\n  =======    "
2707                                "        ===========\n"));
2708   for (i = 0; i < comsize; i++) {
2709     if (ua->argc == 2) {
2710       if (Bstrcasecmp(ua->argk[1], commands[i].key)) {
2711         ua->send->ObjectStart(commands[i].key);
2712         ua->send->ObjectKeyValue("command", commands[i].key, "  %-18s");
2713         ua->send->ObjectKeyValue("description", commands[i].help, " %s\n\n");
2714         ua->send->ObjectKeyValue("arguments", "Arguments:\n\t",
2715                                  commands[i].usage, "%s\n", 40);
2716         ua->send->ObjectKeyValueBool(
2717             "permission", ua->AclAccessOk(Command_ACL, commands[i].key));
2718         ua->send->ObjectEnd(commands[i].key);
2719         break;
2720       }
2721     } else {
2722       if (ua->AclAccessOk(Command_ACL, commands[i].key) &&
2723           (!IsDotCommand(commands[i].key))) {
2724         ua->send->ObjectStart(commands[i].key);
2725         ua->send->ObjectKeyValue("command", commands[i].key, "  %-18s");
2726         ua->send->ObjectKeyValue("description", commands[i].help, " %s\n");
2727         ua->send->ObjectKeyValue("arguments", commands[i].usage, 0);
2728         ua->send->ObjectKeyValueBool("permission", true);
2729         ua->send->ObjectEnd(commands[i].key);
2730       }
2731     }
2732   }
2733   if (i == comsize && ua->argc == 2) {
2734     ua->SendMsg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2735   }
2736   ua->send->Decoration(
2737       _("\nWhen at a prompt, entering a period (.) cancels the command.\n\n"));
2738 
2739   return true;
2740 }
2741 
DotHelpCmd(UaContext * ua,const char * cmd)2742 static bool DotHelpCmd(UaContext* ua, const char* cmd)
2743 {
2744   int i, j;
2745 
2746   /*
2747    * Implement DotHelpCmd here instead of ua_dotcmds.c,
2748    * because comsize and commands are defined here.
2749    */
2750 
2751   /*
2752    * Want to display a specific help section
2753    */
2754   j = FindArgWithValue(ua, NT_("item"));
2755   if (j >= 0 && ua->argk[j]) {
2756     for (i = 0; i < comsize; i++) {
2757       if (bstrcmp(commands[i].key, ua->argv[j])) {
2758         ua->send->ObjectStart(commands[i].key);
2759         ua->send->ObjectKeyValue("command", commands[i].key);
2760         ua->send->ObjectKeyValue("description", commands[i].help);
2761         ua->send->ObjectKeyValue("arguments", commands[i].usage, "%s\n", 0);
2762         ua->send->ObjectKeyValueBool(
2763             "permission", ua->AclAccessOk(Command_ACL, commands[i].key));
2764         ua->send->ObjectEnd(commands[i].key);
2765         break;
2766       }
2767     }
2768     return true;
2769   }
2770 
2771   j = FindArg(ua, NT_("all"));
2772   if (j >= 0) {
2773     /*
2774      * Want to display only user commands (except dot commands)
2775      */
2776     for (i = 0; i < comsize; i++) {
2777       if (ua->AclAccessOk(Command_ACL, commands[i].key) &&
2778           (!IsDotCommand(commands[i].key))) {
2779         ua->send->ObjectStart(commands[i].key);
2780         ua->send->ObjectKeyValue("command", commands[i].key, "%s\n");
2781         ua->send->ObjectKeyValue("description", commands[i].help);
2782         ua->send->ObjectKeyValue("arguments", commands[i].usage, NULL, 0);
2783         ua->send->ObjectKeyValueBool("permission", true);
2784         ua->send->ObjectEnd(commands[i].key);
2785       }
2786     }
2787   } else {
2788     /*
2789      * Want to display everything
2790      */
2791     for (i = 0; i < comsize; i++) {
2792       if (ua->AclAccessOk(Command_ACL, commands[i].key)) {
2793         ua->send->ObjectStart(commands[i].key);
2794         ua->send->ObjectKeyValue("command", commands[i].key, "%s ");
2795         ua->send->ObjectKeyValue("description", commands[i].help, "%s -- ");
2796         ua->send->ObjectKeyValue("arguments", commands[i].usage, "%s\n", 0);
2797         ua->send->ObjectKeyValueBool("permission", true);
2798         ua->send->ObjectEnd(commands[i].key);
2799       }
2800     }
2801   }
2802 
2803   return true;
2804 }
2805 
2806 #if 1
VersionCmd(UaContext * ua,const char * cmd)2807 static bool VersionCmd(UaContext* ua, const char* cmd)
2808 {
2809   ua->send->ObjectStart("version");
2810   ua->send->ObjectKeyValue("name", my_name, "%s ");
2811   ua->send->ObjectKeyValue("type", "bareos-director");
2812   ua->send->ObjectKeyValue("Version", "%s: ", kBareosVersionStrings.Full,
2813                            "%s ");
2814   ua->send->ObjectKeyValue("bdate", kBareosVersionStrings.Date, "(%s) ");
2815   ua->send->ObjectKeyValue("operatingsystem", HOST_OS, "%s ");
2816   ua->send->ObjectKeyValue("distname", DISTNAME, "%s ");
2817   ua->send->ObjectKeyValue("distversion", DISTVER, "%s ");
2818   ua->send->ObjectKeyValue("CustomVersionId", NPRTB(me->verid), "%s\n");
2819   ua->send->ObjectEnd("version");
2820 
2821   return true;
2822 }
2823 #else
2824 /**
2825  *  Test code -- turned on only for debug testing
2826  */
VersionCmd(UaContext * ua,const char * cmd)2827 static bool VersionCmd(UaContext* ua, const char* cmd)
2828 {
2829   dbid_list ids;
2830   PoolMem query(PM_MESSAGE);
2831   OpenDb(ua);
2832   Mmsg(query,
2833        "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and "
2834        "Pool.Name='Full'");
2835   GetQueryDbids(ua->jcr, ua->db, query, ids);
2836   ua->SendMsg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids,
2837               ids.tot_ids);
2838   for (int i = 0; i < ids.num_ids; i++) { ua->SendMsg("id=%d\n", ids.DBId[i]); }
2839   CloseDb(ua);
2840 
2841   return true;
2842 }
2843 #endif
2844 } /* namespace directordaemon */
2845