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