1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Bacula Director -- User Agent Commands
21 *
22 * Kern Sibbald, September MM
23 */
24
25 #include "bacula.h"
26 #include "dird.h"
27
28 /* Imported subroutines */
29
30 /* Imported variables */
31 extern jobq_t job_queue; /* job queue */
32
33
34 /* Imported functions */
35 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
36 extern int gui_cmd(UAContext *ua, const char *cmd);
37 extern int label_cmd(UAContext *ua, const char *cmd);
38 extern int list_cmd(UAContext *ua, const char *cmd);
39 extern int llist_cmd(UAContext *ua, const char *cmd);
40 extern int messagescmd(UAContext *ua, const char *cmd);
41 extern int prunecmd(UAContext *ua, const char *cmd);
42 extern int purge_cmd(UAContext *ua, const char *cmd);
43 extern int truncate_cmd(UAContext *ua, const char *cmd); /* in ua_purge.c */
44 extern int query_cmd(UAContext *ua, const char *cmd);
45 extern int relabel_cmd(UAContext *ua, const char *cmd);
46 extern int restore_cmd(UAContext *ua, const char *cmd);
47 extern int retentioncmd(UAContext *ua, const char *cmd);
48 extern int show_cmd(UAContext *ua, const char *cmd);
49 extern int sqlquery_cmd(UAContext *ua, const char *cmd);
50 extern int status_cmd(UAContext *ua, const char *cmd);
51 extern int update_cmd(UAContext *ua, const char *cmd);
52 extern int collect_cmd(UAContext *ua, const char *cmd);
53
54 /* Forward referenced functions */
55 static int add_cmd(UAContext *ua, const char *cmd);
56 static int automount_cmd(UAContext *ua, const char *cmd);
57 static int cancel_cmd(UAContext *ua, const char *cmd);
58 static int create_cmd(UAContext *ua, const char *cmd);
59 static int delete_cmd(UAContext *ua, const char *cmd);
60 static int disable_cmd(UAContext *ua, const char *cmd);
61 static int enable_cmd(UAContext *ua, const char *cmd);
62 static int estimate_cmd(UAContext *ua, const char *cmd);
63 static int help_cmd(UAContext *ua, const char *cmd);
64 static int memory_cmd(UAContext *ua, const char *cmd);
65 static int mount_cmd(UAContext *ua, const char *cmd);
66 static int release_cmd(UAContext *ua, const char *cmd);
67 static int reload_cmd(UAContext *ua, const char *cmd);
68 static int setdebug_cmd(UAContext *ua, const char *cmd);
69 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
70 static int setip_cmd(UAContext *ua, const char *cmd);
71 static int time_cmd(UAContext *ua, const char *cmd);
72 static int trace_cmd(UAContext *ua, const char *cmd);
73 static int unmount_cmd(UAContext *ua, const char *cmd);
74 static int use_cmd(UAContext *ua, const char *cmd);
75 static int cloud_cmd(UAContext *ua, const char *cmd);
76 static int var_cmd(UAContext *ua, const char *cmd);
77 static int version_cmd(UAContext *ua, const char *cmd);
78 static int wait_cmd(UAContext *ua, const char *cmd);
79
80 static void do_job_delete(UAContext *ua, JobId_t JobId);
81 static int delete_volume(UAContext *ua);
82 static int delete_pool(UAContext *ua);
83 static void delete_job(UAContext *ua);
84 static int delete_client(UAContext *ua);
85 static void do_storage_cmd(UAContext *ua, const char *command);
86
87 int qhelp_cmd(UAContext *ua, const char *cmd);
88 int quit_cmd(UAContext *ua, const char *cmd);
89
90 /* not all in alphabetical order. New commands are added after existing commands with similar letters
91 to prevent breakage of existing user scripts. */
92 struct cmdstruct {
93 const char *key; /* command */
94 int (*func)(UAContext *ua, const char *cmd); /* handler */
95 const char *help; /* main purpose */
96 const char *usage; /* all arguments to build usage */
97 const bool use_in_rs; /* Can use it in Console RunScript */
98 };
99 static struct cmdstruct commands[] = { /* Can use it in Console RunScript*/
100 { NT_("add"), add_cmd, _("Add media to a pool"), NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"), false},
101 { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"), false},
102 { NT_("automount"), automount_cmd, _("Automount after label"), NT_("on | off"), false},
103 { NT_("cancel"), cancel_cmd, _("Cancel a job"), NT_("jobid=<number-list> | job=<job-name> | ujobid=<unique-jobid> | inactive client=<client-name> storage=<storage-name> | all"), false},
104 { NT_("cloud"), cloud_cmd, _("Specific Cloud commands"),
105 NT_("[storage=<storage-name>] [volume=<vol>] [pool=<pool>] [allpools] [allfrompool] [mediatype=<type>] [drive=<number>] [slots=<number] \n"
106 "\tstatus | prune | list | upload | truncate"), true},
107 { NT_("create"), create_cmd, _("Create DB Pool from resource"), NT_("pool=<pool-name>"), false},
108 { NT_("delete"), delete_cmd, _("Delete volume, pool, client or job"), NT_("volume=<vol-name> | pool=<pool-name> | jobid=<id> | client=<client-name> | snapshot"), true},
109 { NT_("disable"), disable_cmd, _("Disable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"), true},
110 { NT_("enable"), enable_cmd, _("Enable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"), true},
111 { NT_("estimate"), estimate_cmd, _("Performs FileSet estimate, listing gives full listing"),
112 NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
113
114 { NT_("exit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
115 { NT_("gui"), gui_cmd, _("Non-interactive gui mode"), NT_("on | off"), false},
116 { NT_("help"), help_cmd, _("Print help on specific command"),
117 NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
118 "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status statistics"
119 "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"
120 "\n\tsnapshot"), false},
121
122 { NT_("label"), label_cmd, _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> drive=<nb> barcodes"), false},
123 { NT_("list"), list_cmd, _("List objects from catalog"),
124 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [limit=<n>]|\n"
125 "\tjobtotals | pools | volume | media <pool=pool-name> | files [type=<deleted|all>] jobid=<nn> | copies jobid=<nn> |\n"
126 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot | \n"
127 "\tfileindex=<mm> | clients\n"
128 ), false},
129
130 { NT_("llist"), llist_cmd, _("Full or long list like list command"),
131 NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [order=<asc/desc>] [limit=<n>]|\n"
132 "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
133 "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot |\n"
134 "\tjobid=<nn> fileindex=<mm> | clients\n"), false},
135
136 { NT_("messages"), messagescmd, _("Display pending messages"), NT_(""), false},
137 { NT_("memory"), memory_cmd, _("Print current memory usage"), NT_(""), true},
138 { NT_("mount"), mount_cmd, _("Mount storage"),
139 NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
140
141 { NT_("prune"), prunecmd, _("Prune expired records from catalog"),
142 NT_("files | jobs | snapshot [client=<client-name>] | client=<client-name> | \n"
143 "\t[expired] [all | allpools | allfrompool] [pool=<pool>] [mediatype=<type>] volume=<volume-name> [yes]"),
144 true},
145
146 { NT_("purge"), purge_cmd, _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
147 { NT_("quit"), quit_cmd, _("Terminate Bconsole session"), NT_(""), false},
148 { NT_("query"), query_cmd, _("Query catalog"), NT_("[<query-item-number>]"), false},
149 { NT_("restore"), restore_cmd, _("Restore files"),
150 NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
151 "restorejob=<job> restoreclient=<cli> noautoparent"
152 "\n\tcomment=<text> jobid=<jobid> jobuser=<user> jobgroup=<grp> copies done select all"), false},
153
154 { NT_("relabel"), relabel_cmd, _("Relabel a tape"),
155 NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
156
157 { NT_("release"), release_cmd, _("Release storage"), NT_("storage=<storage-name> [ device=<device-name> ] "), false},
158 { NT_("reload"), reload_cmd, _("Reload conf file"), NT_(""), true},
159 { NT_("run"), run_cmd, _("Run a job"),
160 NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
161 " where=<directory-prefix>\n\twhen=<universal-time-specification> pool=<pool-name>\n\t"
162 " nextpool=<next-pool-name> comment=<text> accurate=<bool> spooldata=<bool> yes"), false},
163
164 { NT_("restart"), restart_cmd, _("Restart a job"),
165 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
166 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
167
168 { NT_("resume"), restart_cmd, _("Resume a job"),
169 NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
170 "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
171
172 { NT_("status"), status_cmd, _("Report status"),
173 NT_("all | network [bytes=<nn-b>] | dir=<dir-name> | director | client=<client-name> |\n"
174 "\tstorage=<storage-name> slots |\n"
175 "\tschedule [job=<job-name>] [client=<cli-name>] [schedule=<sched-name>] [days=<nn>] [limit=<nn>]\n"
176 "\t\t[time=<universal-time-specification>]"), true},
177
178 { NT_("stop"), cancel_cmd, _("Stop a job"), NT_("jobid=<number-list> job=<job-name> ujobid=<unique-jobid> all"), false},
179 { NT_("setdebug"), setdebug_cmd, _("Sets debug level"),
180 NT_("level=<nn> tags=<tags> trace=0/1 options=<0tTc> tags=<tags> | client=<client-name> | dir | storage=<storage-name> | all"), true},
181
182 { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"),
183 NT_("limit=<speed> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
184
185 { NT_("snapshot"), snapshot_cmd, _("Handle snapshots"),
186 NT_("[client=<client-name> | job=<job-name> | jobid=<jobid>] [delete | list | listclient | prune | sync | update]"), true},
187
188 { NT_("setip"), setip_cmd, _("Sets new client address -- if authorized"), NT_(""), false},
189 { NT_("show"), show_cmd, _("Show resource records"),
190 NT_("job=<xxx> | pool=<yyy> | fileset=<aaa> | schedule=<sss> | client=<zzz> | storage=<sss> | disabled | all"), true},
191
192 { NT_("sqlquery"), sqlquery_cmd, _("Use SQL to query catalog"), NT_(""), false},
193 { NT_("statistics"), collect_cmd, _("Display daemon statistics data"), NT_("[ simple | full | json ] all | <metric>\n"
194 "\t[ client=<client-name> | storage=<storage-name> ]"), true},
195 { NT_("time"), time_cmd, _("Print current time"), NT_(""), true},
196 { NT_("trace"), trace_cmd, _("Turn on/off trace to file"), NT_("on | off"), true},
197 { NT_("truncate"), truncate_cmd, _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"), true},
198 { NT_("unmount"), unmount_cmd, _("Unmount storage"),
199 NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
200
201 { NT_("umount"), unmount_cmd, _("Umount - for old-time Unix guys, see unmount"),
202 NT_("storage=<storage-name> [ drive=<num> ] [ device=<dev-name> ]| jobid=<id> | job=<job-name>"), false},
203
204 { NT_("update"), update_cmd, _("Update volume, pool or stats"),
205 NT_("stats\n\tsnapshot\n\tpool=<poolname>\n\tslots storage=<storage> scan"
206 "\n\tvolume=<volname> volstatus=<status> volretention=<time-def> cacheretention=<time-def>"
207 "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
208 "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
209 "\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
210 "\n\t allfrompool=<pool> fromallpools frompool"),true},
211 { NT_("use"), use_cmd, _("Use catalog xxx"), NT_("catalog=<catalog>"), false},
212 { NT_("var"), var_cmd, _("Does variable expansion"), NT_(""), false},
213 { NT_("version"), version_cmd, _("Print Director version"), NT_(""), true},
214 { NT_("wait"), wait_cmd, _("Wait until no jobs are running"),
215 NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
216 };
217
218 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
219
get_command(int index)220 const char *get_command(int index) {
221 return commands[index].key;
222 }
223
224 /*
225 * Execute a command from the UA
226 */
do_a_command(UAContext * ua)227 bool do_a_command(UAContext *ua)
228 {
229 int i;
230 int len;
231 bool ok = false;
232 bool found = false;
233
234 Dmsg1(900, "Command: %s\n", ua->argk[0]);
235 if (ua->argc == 0) {
236 return false;
237 }
238
239 if (ua->jcr->wstorage) {
240 while (ua->jcr->wstorage->size()) {
241 ua->jcr->wstorage->remove(0);
242 }
243 }
244
245 len = strlen(ua->argk[0]);
246 for (i=0; i<comsize; i++) { /* search for command */
247 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
248 ua->cmd_index = i;
249 /* Check if command permitted, but "quit" is always OK */
250 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
251 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
252 break;
253 }
254 /* Check if this command is authorized in RunScript */
255 if (ua->runscript && !commands[i].use_in_rs) {
256 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
257 break;
258 }
259 if (ua->api) ua->signal(BNET_CMD_BEGIN);
260 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
261 if (ua->api) ua->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
262 found = ua->UA_sock && ua->UA_sock->is_stop() ? false : true;
263 break;
264 }
265 }
266 if (!found) {
267 ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
268 ok = false;
269 }
270 return ok;
271 }
272
273 /*
274 * This is a common routine used to stuff the Pool DB record defaults
275 * into the Media DB record just before creating a media (Volume)
276 * record.
277 */
set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR * mr,POOL_DBR * pr)278 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
279 {
280 mr->PoolId = pr->PoolId;
281 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
282 mr->Recycle = pr->Recycle;
283 mr->VolRetention = pr->VolRetention;
284 mr->CacheRetention = pr->CacheRetention;
285 mr->VolUseDuration = pr->VolUseDuration;
286 mr->ActionOnPurge = pr->ActionOnPurge;
287 mr->RecyclePoolId = pr->RecyclePoolId;
288 mr->MaxVolJobs = pr->MaxVolJobs;
289 mr->MaxVolFiles = pr->MaxVolFiles;
290 mr->MaxVolBytes = pr->MaxVolBytes;
291 mr->LabelType = pr->LabelType;
292 mr->Enabled = 1;
293 }
294
295
296 /*
297 * Add Volumes to an existing Pool
298 */
add_cmd(UAContext * ua,const char * cmd)299 static int add_cmd(UAContext *ua, const char *cmd)
300 {
301 POOL_DBR pr;
302 MEDIA_DBR mr;
303 int num, i, max, startnum;
304 char name[MAX_NAME_LENGTH];
305 STORE *store;
306 int Slot = 0, InChanger = 0;
307
308 ua->send_msg(_(
309 "You probably don't want to be using this command since it\n"
310 "creates database records without labeling the Volumes.\n"
311 "You probably want to use the \"label\" command.\n\n"));
312
313 if (!open_client_db(ua)) {
314 return 1;
315 }
316
317 memset(&pr, 0, sizeof(pr));
318
319 if (!get_pool_dbr(ua, &pr)) {
320 return 1;
321 }
322
323 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
324 pr.MaxVols, pr.PoolType);
325
326 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
327 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
328 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
329 return 1;
330 }
331 pr.MaxVols = ua->pint32_val;
332 }
333
334 /* Get media type */
335 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
336 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
337 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
338 return 1;
339 }
340
341 if (pr.MaxVols == 0) {
342 max = 1000;
343 } else {
344 max = pr.MaxVols - pr.NumVols;
345 }
346 for (;;) {
347 char buf[100];
348 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
349 if (!get_pint(ua, buf)) {
350 return 1;
351 }
352 num = ua->pint32_val;
353 if (num < 0 || num > max) {
354 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
355 continue;
356 }
357 break;
358 }
359
360 for (;;) {
361 if (num == 0) {
362 if (!get_cmd(ua, _("Enter Volume name: "))) {
363 return 1;
364 }
365 } else {
366 if (!get_cmd(ua, _("Enter base volume name: "))) {
367 return 1;
368 }
369 }
370 /* Don't allow | in Volume name because it is the volume separator character */
371 if (!is_volume_name_legal(ua, ua->cmd)) {
372 continue;
373 }
374 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
375 ua->warning_msg(_("Volume name too long.\n"));
376 continue;
377 }
378 if (strlen(ua->cmd) == 0) {
379 ua->warning_msg(_("Volume name must be at least one character long.\n"));
380 continue;
381 }
382 break;
383 }
384
385 bstrncpy(name, ua->cmd, sizeof(name));
386 if (num > 0) {
387 bstrncat(name, "%04d", sizeof(name));
388
389 for (;;) {
390 if (!get_pint(ua, _("Enter the starting number: "))) {
391 return 1;
392 }
393 startnum = ua->pint32_val;
394 if (startnum < 1) {
395 ua->warning_msg(_("Start number must be greater than zero.\n"));
396 continue;
397 }
398 break;
399 }
400 } else {
401 startnum = 1;
402 num = 1;
403 }
404
405 if (store && store->autochanger) {
406 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
407 return 1;
408 }
409 Slot = ua->pint32_val;
410 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
411 return 1;
412 }
413 InChanger = ua->pint32_val;
414 }
415
416 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
417 for (i=startnum; i < num+startnum; i++) {
418 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
419 mr.Slot = Slot++;
420 mr.InChanger = InChanger;
421 mr.Enabled = 1;
422 set_storageid_in_mr(store, &mr);
423 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
424 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
425 ua->error_msg("%s", db_strerror(ua->db));
426 return 1;
427 }
428 // if (i == startnum) {
429 // first_id = mr.PoolId;
430 // }
431 }
432 pr.NumVols += num;
433 Dmsg0(200, "Update pool record.\n");
434 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
435 ua->warning_msg("%s", db_strerror(ua->db));
436 return 1;
437 }
438 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
439
440 return 1;
441 }
442
443 /*
444 * Turn auto mount on/off
445 *
446 * automount on
447 * automount off
448 */
automount_cmd(UAContext * ua,const char * cmd)449 int automount_cmd(UAContext *ua, const char *cmd)
450 {
451 char *onoff;
452
453 if (ua->argc != 2) {
454 if (!get_cmd(ua, _("Turn on or off? "))) {
455 return 1;
456 }
457 onoff = ua->cmd;
458 } else {
459 onoff = ua->argk[1];
460 }
461
462 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
463 return 1;
464 }
465
466 /*
467 * Cancel/Stop a job -- Stop marks it as Incomplete
468 * so that it can be restarted.
469 */
cancel_cmd(UAContext * ua,const char * cmd)470 static int cancel_cmd(UAContext *ua, const char *cmd)
471 {
472 JCR *jcr;
473 bool ret = true;
474 int nb;
475 bool cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
476 alist *jcrs = New(alist(5, not_owned_by_alist));
477
478 /* If the user explicitely ask, we can send the cancel command to
479 * the FD.
480 */
481 if (find_arg(ua, "inactive") > 0) {
482 ret = cancel_inactive_job(ua);
483 goto bail_out;
484 }
485
486 nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
487
488 foreach_alist(jcr, jcrs) {
489 /* Execute the cancel command only if we don't have an error */
490 if (nb != -1) {
491 ret &= cancel_job(ua, jcr, 60, cancel);
492 }
493 free_jcr(jcr);
494 }
495
496 bail_out:
497 delete jcrs;
498 return ret;
499 }
500
501 /*
502 * This is a common routine to create or update a
503 * Pool DB base record from a Pool Resource. We handle
504 * the setting of MaxVols and NumVols slightly differently
505 * depending on if we are creating the Pool or we are
506 * simply bringing it into agreement with the resource (updage).
507 *
508 * Caution : RecyclePoolId isn't setup in this function.
509 * You can use set_pooldbr_recyclepoolid();
510 *
511 */
set_pooldbr_from_poolres(POOL_DBR * pr,POOL * pool,e_pool_op op)512 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
513 {
514 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
515 if (op == POOL_OP_CREATE) {
516 pr->MaxVols = pool->max_volumes;
517 pr->NumVols = 0;
518 } else { /* update pool */
519 if (pr->MaxVols != pool->max_volumes) {
520 pr->MaxVols = pool->max_volumes;
521 }
522 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
523 pr->MaxVols = pr->NumVols;
524 }
525 }
526 pr->LabelType = pool->LabelType;
527 pr->UseOnce = pool->use_volume_once;
528 pr->UseCatalog = pool->use_catalog;
529 pr->Recycle = pool->Recycle;
530 pr->VolRetention = pool->VolRetention;
531 pr->CacheRetention = pool->CacheRetention;
532 pr->VolUseDuration = pool->VolUseDuration;
533 pr->MaxVolJobs = pool->MaxVolJobs;
534 pr->MaxVolFiles = pool->MaxVolFiles;
535 pr->MaxVolBytes = pool->MaxVolBytes;
536 pr->AutoPrune = pool->AutoPrune;
537 pr->ActionOnPurge = pool->action_on_purge;
538 pr->Recycle = pool->Recycle;
539 if (pool->label_format) {
540 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
541 } else {
542 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
543 }
544 }
545
546 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
update_pool_references(JCR * jcr,BDB * db,POOL * pool)547 int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
548 {
549 POOL_DBR pr;
550
551 if (pool->ScratchPool == pool) {
552 Jmsg(NULL, M_WARNING, 0,
553 _("The ScratchPool directive for Pool \"%s\" is incorrect. Using default Scratch pool instead.\n"),
554 pool->name());
555 pool->ScratchPool = NULL;
556 }
557
558 if (!pool->RecyclePool && !pool->ScratchPool) {
559 return 1;
560 }
561
562 memset(&pr, 0, sizeof(POOL_DBR));
563 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
564
565 /* Don't compute NumVols here */
566 if (!db_get_pool_record(jcr, db, &pr)) {
567 return -1; /* not exists in database */
568 }
569
570 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
571
572 if (!set_pooldbr_references(jcr, db, &pr, pool)) {
573 return -1; /* error */
574 }
575
576 /* NumVols is updated here */
577 if (!db_update_pool_record(jcr, db, &pr)) {
578 return -1; /* error */
579 }
580 return 1;
581 }
582
583 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
584 * works with set_pooldbr_from_poolres
585 */
set_pooldbr_references(JCR * jcr,BDB * db,POOL_DBR * pr,POOL * pool)586 bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
587 {
588 POOL_DBR rpool;
589 bool ret = true;
590
591 if (pool->RecyclePool) {
592 memset(&rpool, 0, sizeof(POOL_DBR));
593
594 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
595 if (db_get_pool_record(jcr, db, &rpool)) {
596 pr->RecyclePoolId = rpool.PoolId;
597 } else {
598 Jmsg(jcr, M_WARNING, 0,
599 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
600 "Try to update it with 'update pool=%s'\n"),
601 pool->name(), rpool.Name, rpool.Name,pool->name());
602
603 ret = false;
604 }
605 } else { /* no RecyclePool used, set it to 0 */
606 pr->RecyclePoolId = 0;
607 }
608
609 if (pool->ScratchPool) {
610 memset(&rpool, 0, sizeof(POOL_DBR));
611
612 bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
613 if (db_get_pool_record(jcr, db, &rpool)) {
614 pr->ScratchPoolId = rpool.PoolId;
615 } else {
616 Jmsg(jcr, M_WARNING, 0,
617 _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
618 "Try to update it with 'update pool=%s'\n"),
619 pool->name(), rpool.Name, rpool.Name,pool->name());
620 ret = false;
621 }
622 } else { /* no ScratchPool used, set it to 0 */
623 pr->ScratchPoolId = 0;
624 }
625
626 return ret;
627 }
628
629
630 /*
631 * Create a pool record from a given Pool resource
632 * Also called from backup.c
633 * Returns: -1 on error
634 * 0 record already exists
635 * 1 record created
636 */
637
create_pool(JCR * jcr,BDB * db,POOL * pool,e_pool_op op)638 int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
639 {
640 POOL_DBR pr;
641 memset(&pr, 0, sizeof(POOL_DBR));
642 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
643
644 if (db_get_pool_record(jcr, db, &pr)) {
645 /* Pool Exists */
646 if (op == POOL_OP_UPDATE) { /* update request */
647 set_pooldbr_from_poolres(&pr, pool, op);
648 set_pooldbr_references(jcr, db, &pr, pool);
649 db_update_pool_record(jcr, db, &pr);
650 }
651 return 0; /* exists */
652 }
653
654 set_pooldbr_from_poolres(&pr, pool, op);
655 set_pooldbr_references(jcr, db, &pr, pool);
656
657 if (!db_create_pool_record(jcr, db, &pr)) {
658 return -1; /* error */
659 }
660 return 1;
661 }
662
663
664
665 /*
666 * Create a Pool Record in the database.
667 * It is always created from the Resource record.
668 */
create_cmd(UAContext * ua,const char * cmd)669 static int create_cmd(UAContext *ua, const char *cmd)
670 {
671 POOL *pool;
672
673 if (!open_client_db(ua)) {
674 return 1;
675 }
676
677 pool = get_pool_resource(ua);
678 if (!pool) {
679 return 1;
680 }
681
682 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
683 case 0:
684 ua->error_msg(_("Error: Pool %s already exists.\n"
685 "Use update to change it.\n"), pool->name());
686 break;
687
688 case -1:
689 ua->error_msg("%s", db_strerror(ua->db));
690 break;
691
692 default:
693 break;
694 }
695 ua->send_msg(_("Pool %s created.\n"), pool->name());
696 return 1;
697 }
698
699
700 extern DIRRES *director;
701 extern char *configfile;
702
setbwlimit_client(UAContext * ua,CLIENT * client,char * Job,int64_t limit)703 static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
704 {
705 POOL_MEM buf;
706 CLIENT *old_client;
707 char ed1[50];
708 if (!client) {
709 return 1;
710 }
711
712 /* Connect to File daemon */
713 old_client = ua->jcr->client;
714 ua->jcr->client = client;
715 ua->jcr->max_bandwidth = limit;
716
717 /* Try to connect for 15 seconds */
718 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
719 client->name(), client->address(buf.addr()), client->FDport);
720 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
721 ua->error_msg(_("Failed to connect to Client.\n"));
722 goto bail_out;
723 }
724 Dmsg0(120, "Connected to file daemon\n");
725
726 if (!send_bwlimit(ua->jcr, Job)) {
727 ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
728
729 } else {
730 /* Note, we add 2000 OK that was sent by FD to us to message */
731 ua->info_msg(_("2000 OK Limiting bandwidth to %sB/s %s\n"),
732 edit_uint64_with_suffix(limit, ed1), *Job?Job:_("on running and future jobs"));
733 }
734
735 ua->jcr->file_bsock->signal(BNET_TERMINATE);
736 free_bsock(ua->jcr->file_bsock);
737 ua->jcr->max_bandwidth = 0;
738
739 bail_out:
740 ua->jcr->client = old_client;
741 return 1;
742 }
743
setbwlimit_cmd(UAContext * ua,const char * cmd)744 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
745 {
746 int action = -1;
747 CLIENT *client = NULL;
748 char Job[MAX_NAME_LENGTH];
749 *Job=0;
750 uint64_t limit = 0;
751 JCR *jcr = NULL;
752 int i;
753
754 const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
755 if (find_arg_keyword(ua, lst_all) < 0) {
756 start_prompt(ua, _("Set Bandwidth choice:\n"));
757 add_prompt(ua, _("Running Job")); /* 0 */
758 add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
759 action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
760 NULL, 0);
761 if (action < 0) {
762 return 1;
763 }
764 }
765
766 i = find_arg_with_value(ua, "limit");
767 if (i >= 0) {
768 if (!speed_to_uint64(ua->argv[i], strlen(ua->argv[i]), &limit)) {
769 ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
770 return 1;
771 }
772 } else {
773 if (!get_cmd(ua, _("Enter new bandwidth limit: "))) {
774 return 1;
775 }
776 if (!speed_to_uint64(ua->cmd, strlen(ua->cmd), &limit)) {
777 ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
778 return 1;
779 }
780 }
781
782 const char *lst[] = { "job", "jobid", "jobname", NULL };
783 if (action == 0 || find_arg_keyword(ua, lst) > 0) {
784 alist *jcrs = New(alist(10, not_owned_by_alist));
785 select_running_jobs(ua, jcrs, "limit");
786 foreach_alist(jcr, jcrs) {
787 jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
788 bstrncpy(Job, jcr->Job, sizeof(Job));
789 client = jcr->client;
790 setbwlimit_client(ua, client, Job, limit);
791 free_jcr(jcr);
792 }
793
794 } else {
795 client = get_client_resource(ua, JT_BACKUP_RESTORE);
796 if (client) {
797 setbwlimit_client(ua, client, Job, limit);
798 }
799 }
800 return 1;
801 }
802
803 /*
804 * Set a new address in a Client resource. We do this only
805 * if the Console name is the same as the Client name
806 * and the Console can access the client.
807 */
setip_cmd(UAContext * ua,const char * cmd)808 static int setip_cmd(UAContext *ua, const char *cmd)
809 {
810 CLIENT *client;
811 char addr[1024];
812 if (!ua->cons || !acl_access_client_ok(ua, ua->cons->name(), JT_BACKUP_RESTORE)) {
813 ua->error_msg(_("Unauthorized command from this console.\n"));
814 return 1;
815 }
816 LockRes();
817 client = GetClientResWithName(ua->cons->name());
818
819 if (!client) {
820 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
821 goto get_out;
822 }
823 /* MA Bug 6 remove ifdef */
824 sockaddr_to_ascii(&(ua->UA_sock->client_addr),
825 sizeof(ua->UA_sock->client_addr), addr, sizeof(addr));
826 client->setAddress(addr);
827 ua->send_msg(_("Client \"%s\" address set to %s\n"),
828 client->name(), addr);
829 get_out:
830 UnlockRes();
831 return 1;
832 }
833
834 /*
835 * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
836 * job, client, schedule, storage
837 */
do_enable_disable_cmd(UAContext * ua,bool setting)838 static void do_enable_disable_cmd(UAContext *ua, bool setting)
839 {
840 JOB *job = NULL;
841 CLIENT *client = NULL;
842 SCHED *sched = NULL;
843 int i;
844
845 if (find_arg(ua, NT_("batch")) > 0) {
846 ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
847 db_disable_batch_insert(setting);
848 return;
849 }
850
851 /*
852 * if (find_arg(ua, NT_("scheduler")) > 0) {
853 * ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
854 * return;
855 * }
856 */
857
858 i = find_arg(ua, NT_("job"));
859 if (i >= 0) {
860 if (ua->argv[i]) {
861 LockRes();
862 job = GetJobResWithName(ua->argv[i]);
863 UnlockRes();
864 } else {
865 job = select_enable_disable_job_resource(ua, setting);
866 if (!job) {
867 return;
868 }
869 }
870 }
871 if (job) {
872 if (!acl_access_ok(ua, Job_ACL, job->name())) {
873 ua->error_msg(_("Unauthorized command from this console.\n"));
874 return;
875 }
876 job->setEnabled(setting);
877 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
878 }
879
880 i = find_arg(ua, NT_("client"));
881 if (i >= 0) {
882 if (ua->argv[i]) {
883 LockRes();
884 client = GetClientResWithName(ua->argv[i]);
885 UnlockRes();
886 } else {
887 client = select_enable_disable_client_resource(ua, setting);
888 if (!client) {
889 return;
890 }
891 }
892 }
893 if (client) {
894 if (!acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
895 ua->error_msg(_("Unauthorized command from this console.\n"));
896 return;
897 }
898 client->setEnabled(setting);
899 ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
900 }
901
902 i = find_arg(ua, NT_("schedule"));
903 if (i >= 0) {
904 if (ua->argv[i]) {
905 LockRes();
906 sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
907 UnlockRes();
908 } else {
909 sched = select_enable_disable_schedule_resource(ua, setting);
910 if (!sched) {
911 return;
912 }
913 }
914 }
915 if (sched) {
916 if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
917 ua->error_msg(_("Unauthorized command from this console.\n"));
918 return;
919 }
920 sched->setEnabled(setting);
921 ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
922 }
923
924 i = find_arg(ua, NT_("storage"));
925 if (i >= 0) {
926 do_storage_cmd(ua, setting?"enable":"disable");
927 }
928
929 if (i < 0 && !sched && !client && !job) {
930 ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
931 }
932
933 return;
934 }
935
enable_cmd(UAContext * ua,const char * cmd)936 static int enable_cmd(UAContext *ua, const char *cmd)
937 {
938 do_enable_disable_cmd(ua, true);
939 return 1;
940 }
941
disable_cmd(UAContext * ua,const char * cmd)942 static int disable_cmd(UAContext *ua, const char *cmd)
943 {
944 do_enable_disable_cmd(ua, false);
945 return 1;
946 }
947
do_dir_setdebug(UAContext * ua,int64_t level,int trace_flag,char * options,int64_t tags)948 static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
949 {
950 debug_level = level;
951 debug_level_tags = tags;
952 set_trace(trace_flag);
953 set_debug_flags(options);
954 }
955
do_storage_setdebug(UAContext * ua,STORE * store,int64_t level,int trace_flag,int hangup,int blowup,char * options,char * tags)956 static void do_storage_setdebug(UAContext *ua, STORE *store,
957 int64_t level, int trace_flag, int hangup, int blowup,
958 char *options, char *tags)
959 {
960 BSOCK *sd;
961 USTORE lstore;
962
963 lstore.store = store;
964 pm_strcpy(lstore.store_source, _("unknown source"));
965 set_wstorage(ua->jcr, &lstore);
966 /* Try connecting for up to 15 seconds */
967 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
968 store->name(), store->address, store->SDport);
969 if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
970 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
971 return;
972 }
973 Dmsg0(120, _("Connected to storage daemon\n"));
974 sd = ua->jcr->store_bsock;
975 sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
976 (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
977 if (sd->recv() >= 0) {
978 ua->send_msg("%s", sd->msg);
979 }
980 sd->signal(BNET_TERMINATE);
981 free_bsock(ua->jcr->store_bsock);
982 return;
983 }
984
985 /*
986 * For the client, we have the following values that can be set
987 * level = debug level
988 * trace = send debug output to a file
989 * options = various options for debug or specific FD behavior
990 * hangup = how many records to send to FD before hanging up
991 * obviously this is most useful for testing restarting
992 * failed jobs.
993 * blowup = how many records to send to FD before blowing up the FD.
994 */
do_client_setdebug(UAContext * ua,CLIENT * client,int64_t level,int trace,int hangup,int blowup,char * options,char * tags)995 static void do_client_setdebug(UAContext *ua, CLIENT *client,
996 int64_t level, int trace, int hangup, int blowup,
997 char *options, char *tags)
998 {
999 POOL_MEM buf;
1000 CLIENT *old_client;
1001 BSOCK *fd;
1002
1003 /* Connect to File daemon */
1004
1005 old_client = ua->jcr->client;
1006 ua->jcr->client = client;
1007 /* Try to connect for 15 seconds */
1008 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1009 client->name(), client->address(buf.addr()), client->FDport);
1010
1011 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1012 ua->error_msg(_("Failed to connect to Client.\n"));
1013 ua->jcr->client = old_client;
1014 return;
1015 }
1016 Dmsg0(120, "Connected to file daemon\n");
1017
1018 fd = ua->jcr->file_bsock;
1019 if (ua->jcr->FDVersion <= 10) {
1020 fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
1021 (int32_t)level, trace, hangup);
1022 } else {
1023 fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
1024 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
1025 }
1026 if (fd->recv() >= 0) {
1027 ua->send_msg("%s", fd->msg);
1028 }
1029 fd->signal(BNET_TERMINATE);
1030 free_bsock(ua->jcr->file_bsock);
1031 ua->jcr->client = old_client;
1032 return;
1033 }
1034
1035
do_all_setdebug(UAContext * ua,int64_t level,int trace_flag,int hangup,int blowup,char * options,char * tags)1036 static void do_all_setdebug(UAContext *ua, int64_t level,
1037 int trace_flag, int hangup, int blowup,
1038 char *options, char *tags)
1039 {
1040 STORE *store, **unique_store;
1041 CLIENT *client, **unique_client;
1042 POOL_MEM buf1, buf2;
1043 int i, j, found;
1044 int64_t t=0;
1045
1046 /* Director */
1047 debug_parse_tags(tags, &t);
1048 do_dir_setdebug(ua, level, trace_flag, options, t);
1049
1050 /* Count Storage items */
1051 LockRes();
1052 store = NULL;
1053 i = 0;
1054 foreach_res(store, R_STORAGE) {
1055 i++;
1056 }
1057 unique_store = (STORE **) malloc(i * sizeof(STORE));
1058 /* Find Unique Storage address/port */
1059 store = (STORE *)GetNextRes(R_STORAGE, NULL);
1060 i = 0;
1061 unique_store[i++] = store;
1062 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1063 found = 0;
1064 for (j=0; j<i; j++) {
1065 if (strcmp(unique_store[j]->address, store->address) == 0 &&
1066 unique_store[j]->SDport == store->SDport) {
1067 found = 1;
1068 break;
1069 }
1070 }
1071 if (!found) {
1072 unique_store[i++] = store;
1073 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1074 }
1075 }
1076 UnlockRes();
1077
1078 /* Call each unique Storage daemon */
1079 for (j=0; j<i; j++) {
1080 do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1081 hangup, blowup, options, tags);
1082 }
1083 free(unique_store);
1084
1085 /* Count Client items */
1086 LockRes();
1087 client = NULL;
1088 i = 0;
1089 foreach_res(client, R_CLIENT) {
1090 i++;
1091 }
1092 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1093 /* Find Unique Client address/port */
1094 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1095 i = 0;
1096 unique_client[i++] = client;
1097 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1098 found = 0;
1099 for (j=0; j<i; j++) {
1100 if (strcmp(unique_client[j]->address(buf1.addr()), client->address(buf2.addr())) == 0 &&
1101 unique_client[j]->FDport == client->FDport) {
1102 found = 1;
1103 break;
1104 }
1105 }
1106 if (!found) {
1107 unique_client[i++] = client;
1108 Dmsg2(140, "Stuffing: %s:%d\n", client->address(buf1.addr()), client->FDport);
1109 }
1110 }
1111 UnlockRes();
1112
1113 /* Call each unique File daemon */
1114 for (j=0; j<i; j++) {
1115 do_client_setdebug(ua, unique_client[j], level, trace_flag,
1116 hangup, blowup, options, tags);
1117 }
1118 free(unique_client);
1119 }
1120
1121 /*
1122 * setdebug level=nn all trace=1/0
1123 */
setdebug_cmd(UAContext * ua,const char * cmd)1124 static int setdebug_cmd(UAContext *ua, const char *cmd)
1125 {
1126 STORE *store;
1127 CLIENT *client;
1128 int64_t level=0, tags=0;
1129 int trace_flag = -1;
1130 int hangup = -1;
1131 int blowup = -1;
1132 int i;
1133 char *tags_str=NULL;
1134 char options[60];
1135
1136 Dmsg1(120, "setdebug:%s:\n", cmd);
1137
1138 *options = 0;
1139 i = find_arg_with_value(ua, "options");
1140 if (i >= 0) {
1141 bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1142 }
1143 level = -1;
1144 i = find_arg_with_value(ua, "level");
1145 if (i >= 0) {
1146 level = str_to_int64(ua->argv[i]);
1147 }
1148 if (level < 0) {
1149 if (!get_pint(ua, _("Enter new debug level: "))) {
1150 return 1;
1151 }
1152 level = ua->pint32_val;
1153 }
1154
1155 /* Better to send the tag string instead of tweaking the level
1156 * in case where we extend the tag or change the representation
1157 */
1158 i = find_arg_with_value(ua, "tags");
1159 if (i > 0) {
1160 tags_str = ua->argv[i];
1161 if (!debug_parse_tags(tags_str, &tags)) {
1162 ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1163 return 1;
1164 }
1165 }
1166
1167 /* Look for trace flag. -1 => not change */
1168 i = find_arg_with_value(ua, "trace");
1169 if (i >= 0) {
1170 trace_flag = atoi(ua->argv[i]);
1171 if (trace_flag > 0) {
1172 trace_flag = 1;
1173 }
1174 }
1175
1176 /* Look for hangup (debug only) flag. -1 => not change */
1177 i = find_arg_with_value(ua, "hangup");
1178 if (i >= 0) {
1179 hangup = atoi(ua->argv[i]);
1180 }
1181
1182 /* Look for blowup (debug only) flag. -1 => not change */
1183 i = find_arg_with_value(ua, "blowup");
1184 if (i >= 0) {
1185 blowup = atoi(ua->argv[i]);
1186 }
1187
1188 /* General debug? */
1189 for (i=1; i<ua->argc; i++) {
1190 if (strcasecmp(ua->argk[i], "all") == 0) {
1191 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1192 return 1;
1193 }
1194 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1195 strcasecmp(ua->argk[i], "director") == 0) {
1196 do_dir_setdebug(ua, level, trace_flag, options, tags);
1197 return 1;
1198 }
1199 if (strcasecmp(ua->argk[i], "client") == 0 ||
1200 strcasecmp(ua->argk[i], "fd") == 0) {
1201 client = NULL;
1202 if (ua->argv[i]) {
1203 client = GetClientResWithName(ua->argv[i]);
1204 if (client) {
1205 do_client_setdebug(ua, client, level, trace_flag,
1206 hangup, blowup, options, tags_str);
1207 return 1;
1208 }
1209 }
1210 client = select_client_resource(ua, JT_BACKUP_RESTORE);
1211 if (client) {
1212 do_client_setdebug(ua, client, level, trace_flag,
1213 hangup, blowup, options, tags_str);
1214 return 1;
1215 }
1216 }
1217
1218 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1219 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1220 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1221 store = NULL;
1222 if (ua->argv[i]) {
1223 store = GetStoreResWithName(ua->argv[i]);
1224 if (store) {
1225 do_storage_setdebug(ua, store, level, trace_flag,
1226 hangup, blowup, options, tags_str);
1227 return 1;
1228 }
1229 }
1230 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1231 if (store) {
1232 do_storage_setdebug(ua, store, level, trace_flag,
1233 hangup, blowup, options, tags_str);
1234 return 1;
1235 }
1236 }
1237 }
1238 /*
1239 * We didn't find an appropriate keyword above, so
1240 * prompt the user.
1241 */
1242 start_prompt(ua, _("Available daemons are: \n"));
1243 add_prompt(ua, _("Director"));
1244 add_prompt(ua, _("Storage"));
1245 add_prompt(ua, _("Client"));
1246 add_prompt(ua, _("All"));
1247 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1248 case 0: /* Director */
1249 do_dir_setdebug(ua, level, trace_flag, options, tags);
1250 break;
1251 case 1:
1252 store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1253 if (store) {
1254 do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1255 options, tags_str);
1256 }
1257 break;
1258 case 2:
1259 client = select_client_resource(ua, JT_BACKUP_RESTORE);
1260 if (client) {
1261 do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1262 options, tags_str);
1263 }
1264 break;
1265 case 3:
1266 do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1267 break;
1268 default:
1269 break;
1270 }
1271 return 1;
1272 }
1273
1274 /*
1275 * Turn debug tracing to file on/off
1276 */
trace_cmd(UAContext * ua,const char * cmd)1277 static int trace_cmd(UAContext *ua, const char *cmd)
1278 {
1279 char *onoff;
1280
1281 if (ua->argc != 2) {
1282 if (!get_cmd(ua, _("Turn on or off? "))) {
1283 return 1;
1284 }
1285 onoff = ua->cmd;
1286 } else {
1287 onoff = ua->argk[1];
1288 }
1289
1290 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1291 return 1;
1292 }
1293
var_cmd(UAContext * ua,const char * cmd)1294 static int var_cmd(UAContext *ua, const char *cmd)
1295 {
1296 POOLMEM *val = get_pool_memory(PM_FNAME);
1297 char *var;
1298
1299 if (!open_client_db(ua)) {
1300 return 1;
1301 }
1302 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1303 var++;
1304 }
1305 while (*var == ' ') { /* skip spaces */
1306 var++;
1307 }
1308 Dmsg1(100, "Var=%s:\n", var);
1309 variable_expansion(ua->jcr, var, &val);
1310 ua->send_msg("%s\n", val);
1311 free_pool_memory(val);
1312 return 1;
1313 }
1314
estimate_cmd(UAContext * ua,const char * cmd)1315 static int estimate_cmd(UAContext *ua, const char *cmd)
1316 {
1317 JOB *job = NULL;
1318 CLIENT *client = NULL;
1319 FILESET *fileset = NULL;
1320 POOL_MEM buf;
1321 int listing = 0;
1322 char since[MAXSTRING];
1323 JCR *jcr = ua->jcr;
1324 int accurate=-1;
1325
1326 jcr->setJobType(JT_BACKUP);
1327 jcr->start_time = time(NULL);
1328 jcr->setJobLevel(L_FULL);
1329
1330 for (int i=1; i<ua->argc; i++) {
1331 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1332 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1333 if (ua->argv[i]) {
1334 client = GetClientResWithName(ua->argv[i]);
1335 if (!client) {
1336 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1337 return 1;
1338 }
1339 if (!acl_access_client_ok(ua, client->name(), JT_BACKUP)) {
1340 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1341 return 1;
1342 }
1343 continue;
1344 } else {
1345 ua->error_msg(_("Client name missing.\n"));
1346 return 1;
1347 }
1348 }
1349 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1350 if (ua->argv[i]) {
1351 job = GetJobResWithName(ua->argv[i]);
1352 if (!job) {
1353 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1354 return 1;
1355 }
1356 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1357 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1358 return 1;
1359 }
1360 continue;
1361 } else {
1362 ua->error_msg(_("Job name missing.\n"));
1363 return 1;
1364 }
1365
1366 }
1367 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1368 if (ua->argv[i]) {
1369 fileset = GetFileSetResWithName(ua->argv[i]);
1370 if (!fileset) {
1371 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1372 return 1;
1373 }
1374 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1375 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1376 return 1;
1377 }
1378 continue;
1379 } else {
1380 ua->error_msg(_("Fileset name missing.\n"));
1381 return 1;
1382 }
1383 }
1384 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1385 listing = 1;
1386 continue;
1387 }
1388 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1389 if (ua->argv[i]) {
1390 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1391 ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1392 return 1;
1393 }
1394 continue;
1395 } else {
1396 ua->error_msg(_("Level value missing.\n"));
1397 return 1;
1398 }
1399 }
1400 if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1401 if (ua->argv[i]) {
1402 if (!is_yesno(ua->argv[i], &accurate)) {
1403 ua->error_msg(_("Invalid value for accurate. "
1404 "It must be yes or no.\n"));
1405 return 1;
1406 }
1407 continue;
1408 } else {
1409 ua->error_msg(_("Accurate value missing.\n"));
1410 return 1;
1411 }
1412 }
1413 }
1414 if (!job && !(client && fileset)) {
1415 if (!(job = select_job_resource(ua))) {
1416 return 1;
1417 }
1418 }
1419 if (!job) {
1420 job = GetJobResWithName(ua->argk[1]);
1421 if (!job) {
1422 ua->error_msg(_("No job specified.\n"));
1423 return 1;
1424 }
1425 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1426 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1427 return 1;
1428 }
1429 }
1430 jcr->job = job;
1431 if (!client) {
1432 client = job->client;
1433 }
1434 if (!fileset) {
1435 fileset = job->fileset;
1436 }
1437 jcr->client = client;
1438 jcr->fileset = fileset;
1439 close_db(ua);
1440 if (job->pool->catalog) {
1441 ua->catalog = job->pool->catalog;
1442 } else {
1443 ua->catalog = client->catalog;
1444 }
1445
1446 if (!open_db(ua)) {
1447 return 1;
1448 }
1449
1450 init_jcr_job_record(jcr);
1451
1452 if (!get_or_create_client_record(jcr)) {
1453 return 1;
1454 }
1455 if (!get_or_create_fileset_record(jcr)) {
1456 return 1;
1457 }
1458
1459 get_level_since_time(ua->jcr, since, sizeof(since));
1460
1461 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1462 jcr->client->name(), jcr->client->address(buf.addr()), jcr->client->FDport);
1463 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1464 ua->error_msg(_("Failed to connect to Client.\n"));
1465 return 1;
1466 }
1467
1468 /* The level string change if accurate mode is enabled */
1469 if (accurate >= 0) {
1470 jcr->accurate = accurate;
1471 } else {
1472 jcr->accurate = job->accurate;
1473 }
1474
1475 if (!send_level_command(jcr)) {
1476 goto bail_out;
1477 }
1478
1479 if (!send_include_list(jcr)) {
1480 ua->error_msg(_("Error sending include list.\n"));
1481 goto bail_out;
1482 }
1483
1484 if (!send_exclude_list(jcr)) {
1485 ua->error_msg(_("Error sending exclude list.\n"));
1486 goto bail_out;
1487 }
1488
1489 /*
1490 * If the job is in accurate mode, we send the list of
1491 * all files to FD.
1492 */
1493 Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1494 if (!send_accurate_current_files(jcr)) {
1495 goto bail_out;
1496 }
1497
1498 jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1499 while (jcr->file_bsock->recv() >= 0) {
1500 ua->send_msg("%s", jcr->file_bsock->msg);
1501 }
1502
1503 bail_out:
1504 if (jcr->file_bsock) {
1505 jcr->file_bsock->signal(BNET_TERMINATE);
1506 free_bsock(ua->jcr->file_bsock);
1507 }
1508 return 1;
1509 }
1510
1511 /*
1512 * print time
1513 */
time_cmd(UAContext * ua,const char * cmd)1514 static int time_cmd(UAContext *ua, const char *cmd)
1515 {
1516 char sdt[50];
1517 time_t ttime = time(NULL);
1518 struct tm tm;
1519 (void)localtime_r(&ttime, &tm);
1520 strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1521 ua->send_msg("%s\n", sdt);
1522 return 1;
1523 }
1524
1525 /*
1526 * reload the conf file
1527 */
1528 extern "C" void reload_config(int sig);
1529
reload_cmd(UAContext * ua,const char * cmd)1530 static int reload_cmd(UAContext *ua, const char *cmd)
1531 {
1532 reload_config(1);
1533 return 1;
1534 }
1535
1536 /*
1537 * Delete Pool records (should purge Media with it).
1538 *
1539 * delete pool=<pool-name>
1540 * delete volume pool=<pool-name> volume=<name>
1541 * delete jobid=xxx
1542 */
delete_cmd(UAContext * ua,const char * cmd)1543 static int delete_cmd(UAContext *ua, const char *cmd)
1544 {
1545 static const char *keywords[] = {
1546 NT_("volume"),
1547 NT_("pool"),
1548 NT_("jobid"),
1549 NT_("snapshot"),
1550 NT_("client"),
1551 NULL};
1552
1553 /* Deleting large jobs can take time! */
1554 if (!open_new_client_db(ua)) {
1555 return 1;
1556 }
1557
1558 switch (find_arg_keyword(ua, keywords)) {
1559 case 0:
1560 delete_volume(ua);
1561 return 1;
1562 case 1:
1563 delete_pool(ua);
1564 return 1;
1565 case 2:
1566 int i;
1567 while ((i=find_arg(ua, "jobid")) > 0) {
1568 delete_job(ua);
1569 *ua->argk[i] = 0; /* zap keyword already visited */
1570 }
1571 return 1;
1572 case 3:
1573 delete_snapshot(ua);
1574 return 1;
1575 case 4:
1576 delete_client(ua);
1577 return 1;
1578 default:
1579 break;
1580 }
1581
1582 ua->warning_msg(_(
1583 "In general it is not a good idea to delete either a\n"
1584 "Pool or a Volume since they may contain data.\n\n"));
1585
1586 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1587 case 0:
1588 delete_volume(ua);
1589 break;
1590 case 1:
1591 delete_pool(ua);
1592 break;
1593 case 2:
1594 delete_job(ua);
1595 return 1;
1596 case 3:
1597 delete_snapshot(ua);
1598 return 1;
1599 case 4:
1600 delete_client(ua);
1601 return 1;
1602 default:
1603 ua->warning_msg(_("Nothing done.\n"));
1604 break;
1605 }
1606 return 1;
1607 }
1608
1609 /*
1610 * delete_job has been modified to parse JobID lists like the
1611 * following:
1612 * delete JobID=3,4,6,7-11,14
1613 *
1614 * Thanks to Phil Stracchino for the above addition.
1615 */
delete_job(UAContext * ua)1616 static void delete_job(UAContext *ua)
1617 {
1618 int JobId; /* not JobId_t because it's unsigned and not compatible with sellist */
1619 char buf[256];
1620 sellist sl;
1621
1622 int i = find_arg_with_value(ua, NT_("jobid"));
1623 if (i >= 0) {
1624 if (!sl.set_string(ua->argv[i], true)) {
1625 ua->warning_msg("%s", sl.get_errmsg());
1626 return;
1627 }
1628
1629 if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1630 bsnprintf(buf, sizeof(buf),
1631 _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1632 if (!get_yesno(ua, buf) || ua->pint32_val==0) {
1633 return;
1634 }
1635 }
1636
1637 foreach_sellist(JobId, &sl) {
1638 do_job_delete(ua, JobId);
1639 }
1640
1641 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1642 return;
1643
1644 } else {
1645 JobId = ua->int64_val;
1646 do_job_delete(ua, JobId);
1647 }
1648 }
1649
1650 /*
1651 * do_job_delete now performs the actual delete operation atomically
1652 */
do_job_delete(UAContext * ua,JobId_t JobId)1653 static void do_job_delete(UAContext *ua, JobId_t JobId)
1654 {
1655 char ed1[50];
1656
1657 edit_int64(JobId, ed1);
1658 purge_jobs_from_catalog(ua, ed1);
1659 ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1660 }
1661
1662 /*
1663 * Delete media records from database -- dangerous
1664 */
delete_volume(UAContext * ua)1665 static int delete_volume(UAContext *ua)
1666 {
1667 MEDIA_DBR mr;
1668 char buf[1000];
1669 db_list_ctx lst;
1670
1671 if (!select_media_dbr(ua, &mr)) {
1672 return 1;
1673 }
1674 ua->warning_msg(_("\nThis command will delete volume %s\n"
1675 "and all Jobs saved on that volume from the Catalog\n"),
1676 mr.VolumeName);
1677
1678 if (find_arg(ua, "yes") >= 0) {
1679 ua->pint32_val = 1; /* Have "yes" on command line already" */
1680 } else {
1681 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1682 mr.VolumeName);
1683 if (!get_yesno(ua, buf)) {
1684 return 1;
1685 }
1686 }
1687 if (!ua->pint32_val) {
1688 return 1;
1689 }
1690
1691 /* If not purged, do it */
1692 if (strcmp(mr.VolStatus, "Purged") != 0) {
1693 if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1694 ua->error_msg(_("Can't list jobs on this volume\n"));
1695 return 1;
1696 }
1697 if (lst.count) {
1698 purge_jobs_from_catalog(ua, lst.list);
1699 }
1700 }
1701
1702 db_delete_media_record(ua->jcr, ua->db, &mr);
1703 return 1;
1704 }
1705
1706 /*
1707 * Delete a pool record from the database -- dangerous
1708 * TODO: Check if the resource is still defined?
1709 */
delete_pool(UAContext * ua)1710 static int delete_pool(UAContext *ua)
1711 {
1712 POOL_DBR pr;
1713 char buf[200];
1714
1715 memset(&pr, 0, sizeof(pr));
1716
1717 if (!get_pool_dbr(ua, &pr)) {
1718 return 1;
1719 }
1720 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1721 pr.Name);
1722 if (!get_yesno(ua, buf)) {
1723 return 1;
1724 }
1725 if (ua->pint32_val) {
1726 db_delete_pool_record(ua->jcr, ua->db, &pr);
1727 }
1728 return 1;
1729 }
1730
1731 /*
1732 * Delete a client record from the database
1733 */
delete_client(UAContext * ua)1734 static int delete_client(UAContext *ua)
1735 {
1736 CLIENT *client;
1737 CLIENT_DBR cr;
1738 char buf[200];
1739 db_list_ctx lst;
1740
1741 memset(&cr, 0, sizeof(cr));
1742
1743 if (!get_client_dbr(ua, &cr, 0)) {
1744 return 1;
1745 }
1746
1747 client = (CLIENT*) GetResWithName(R_CLIENT, cr.Name);
1748
1749 if (client) {
1750 ua->error_msg(_("Unable to delete Client \"%s\", the resource is still defined in the configuration.\n"), cr.Name);
1751 return 1;
1752 }
1753
1754 if (!db_get_client_jobids(ua->jcr, ua->db, &cr, &lst)) {
1755 ua->error_msg(_("Can't list jobs on this client\n"));
1756 return 1;
1757 }
1758
1759 if (find_arg(ua, "yes") > 0) {
1760 ua->pint32_val = 1;
1761
1762 } else {
1763 if (lst.count == 0) {
1764 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Client \"%s\? (yes/no): "), cr.Name);
1765 } else {
1766 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Client \"%s\" and purge %d job(s)? (yes/no): "), cr.Name, lst.count);
1767 }
1768 if (!get_yesno(ua, buf)) {
1769 return 1;
1770 }
1771 }
1772
1773 if (ua->pint32_val) {
1774 if (lst.count) {
1775 ua->send_msg(_("Purging %d job(s).\n"), lst.count);
1776 purge_jobs_from_catalog(ua, lst.list);
1777 }
1778 ua->send_msg(_("Deleting client \"%s\".\n"), cr.Name);
1779 db_delete_client_record(ua->jcr, ua->db, &cr);
1780 }
1781 return 1;
1782 }
1783
memory_cmd(UAContext * ua,const char * cmd)1784 int memory_cmd(UAContext *ua, const char *cmd)
1785 {
1786 garbage_collect_memory();
1787 list_dir_status_header(ua);
1788 sm_dump(false, true);
1789 return 1;
1790 }
1791
do_storage_cmd(UAContext * ua,const char * command)1792 static void do_storage_cmd(UAContext *ua, const char *command)
1793 {
1794 USTORE store;
1795 BSOCK *sd;
1796 JCR *jcr = ua->jcr;
1797 char dev_name[MAX_NAME_LENGTH];
1798 int drive, i;
1799 int slot;
1800
1801 if (!open_client_db(ua)) {
1802 return;
1803 }
1804 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1805
1806 store.store = get_storage_resource(ua, true/*arg is storage*/);
1807 if (!store.store) {
1808 return;
1809 }
1810 pm_strcpy(store.store_source, _("unknown source"));
1811 set_wstorage(jcr, &store);
1812 drive = get_storage_drive(ua, store.store);
1813 /* For the disable/enable/unmount commands, the slot is not mandatory */
1814 if (strcasecmp(command, "disable") == 0 ||
1815 strcasecmp(command, "enable") == 0 ||
1816 strcasecmp(command, "unmount") == 0) {
1817 slot = 0;
1818 } else {
1819 slot = get_storage_slot(ua, store.store);
1820 }
1821 /* Users may set a device name directly on the command line */
1822 if ((i = find_arg_with_value(ua, "device")) > 0) {
1823 POOLMEM *errmsg = get_pool_memory(PM_NAME);
1824 if (!is_name_valid(ua->argv[i], &errmsg)) {
1825 ua->error_msg(_("Invalid device name. %s"), errmsg);
1826 free_pool_memory(errmsg);
1827 return;
1828 }
1829 free_pool_memory(errmsg);
1830 bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1831
1832 } else { /* We take the default device name */
1833 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1834 }
1835
1836 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1837 store.store->media_type, store.store->dev_name(), drive);
1838 Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1839
1840 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1841 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1842 return;
1843 }
1844 sd = jcr->store_bsock;
1845 bash_spaces(dev_name);
1846 sd->fsend("%s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1847 while (sd->recv() >= 0) {
1848 ua->send_msg("%s", sd->msg);
1849 }
1850 sd->signal(BNET_TERMINATE);
1851 free_bsock(ua->jcr->store_bsock);
1852 }
1853
1854 /*
1855 * mount [storage=<name>] [drive=nn] [slot=mm]
1856 */
mount_cmd(UAContext * ua,const char * cmd)1857 static int mount_cmd(UAContext *ua, const char *cmd)
1858 {
1859 do_storage_cmd(ua, "mount") ; /* mount */
1860 return 1;
1861 }
1862
1863
1864 /*
1865 * unmount [storage=<name>] [drive=nn]
1866 */
unmount_cmd(UAContext * ua,const char * cmd)1867 static int unmount_cmd(UAContext *ua, const char *cmd)
1868 {
1869 do_storage_cmd(ua, "unmount"); /* unmount */
1870 return 1;
1871 }
1872
1873
1874 /*
1875 * release [storage=<name>] [drive=nn]
1876 */
release_cmd(UAContext * ua,const char * cmd)1877 static int release_cmd(UAContext *ua, const char *cmd)
1878 {
1879 do_storage_cmd(ua, "release"); /* release */
1880 return 1;
1881 }
1882
1883 /*
1884 * cloud functions, like to upload cached parts to cloud.
1885 */
cloud_volumes_cmd(UAContext * ua,const char * cmd,const char * mode)1886 int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode)
1887 {
1888 int drive = -1;
1889 int nb = 0;
1890 uint32_t *results = NULL;
1891 MEDIA_DBR mr;
1892 POOL_DBR pr;
1893 BSOCK *sd = NULL;
1894 char storage[MAX_NAME_LENGTH];
1895 const char *action = mode;
1896 memset(&pr, 0, sizeof(pr));
1897
1898 /*
1899 * Look for all volumes that are enabled and
1900 * have more the 200 bytes.
1901 */
1902 mr.Enabled = 1;
1903 mr.Recycle = -1; /* All Recycle status */
1904 if (strcmp("prunecache", mode) == 0) {
1905 mr.CacheRetention = 1;
1906 action = "truncate cache";
1907 }
1908
1909 if (!scan_storage_cmd(ua, cmd, false, /* fromallpool*/
1910 &drive, &mr, &pr, NULL, storage,
1911 &nb, &results))
1912 {
1913 goto bail_out;
1914 }
1915
1916 if ((sd=open_sd_bsock(ua)) == NULL) {
1917 Dmsg0(100, "Can't open connection to sd\n");
1918 goto bail_out;
1919 }
1920
1921 /*
1922 * Loop over the candidate Volumes and upload parts
1923 */
1924 for (int i=0; i < nb; i++) {
1925 bool ok=false;
1926 mr.clear();
1927 mr.MediaId = results[i];
1928 if (!db_get_media_record(ua->jcr, ua->db, &mr)) {
1929 goto bail_out;
1930 }
1931
1932 /* Protect us from spaces */
1933 bash_spaces(mr.VolumeName);
1934 bash_spaces(mr.MediaType);
1935 bash_spaces(pr.Name);
1936 bash_spaces(storage);
1937
1938 sd->fsend("%s Storage=%s Volume=%s PoolName=%s MediaType=%s "
1939 "Slot=%d drive=%d CacheRetention=%lld\n",
1940 action, storage, mr.VolumeName, pr.Name, mr.MediaType,
1941 mr.Slot, drive, mr.CacheRetention);
1942
1943 unbash_spaces(mr.VolumeName);
1944 unbash_spaces(mr.MediaType);
1945 unbash_spaces(pr.Name);
1946 unbash_spaces(storage);
1947
1948 /* Check for valid response */
1949 while (bget_dirmsg(sd) >= 0) {
1950 if (strncmp(sd->msg, "3000 OK truncate cache", 22) == 0) {
1951 ua->send_msg("%s", sd->msg);
1952 ok = true;
1953
1954 } else if (strncmp(sd->msg, "3000 OK", 7) == 0) {
1955 ua->send_msg(_("The volume \"%s\" has been uploaded\n"), mr.VolumeName);
1956 ok = true;
1957
1958
1959 } else if (strncmp(sd->msg, "39", 2) == 0) {
1960 ua->warning_msg("%s", sd->msg);
1961
1962 } else {
1963 ua->send_msg("%s", sd->msg);
1964 }
1965 }
1966 if (!ok) {
1967 ua->warning_msg(_("Unable to %s for volume \"%s\"\n"), action, mr.VolumeName);
1968 }
1969 }
1970
1971 bail_out:
1972 close_db(ua);
1973 close_sd_bsock(ua);
1974 ua->jcr->wstore = NULL;
1975 if (results) {
1976 free(results);
1977 }
1978
1979 return 1;
1980 }
1981
1982 /* List volumes in the cloud */
1983 /* TODO: Update the code for .api 2 and llist */
cloud_list_cmd(UAContext * ua,const char * cmd)1984 static int cloud_list_cmd(UAContext *ua, const char *cmd)
1985 {
1986 int drive = -1;
1987 int64_t size, mtime;
1988 STORE *store = NULL;
1989 MEDIA_DBR mr;
1990 POOL_DBR pr;
1991 BSOCK *sd = NULL;
1992 char storage[MAX_NAME_LENGTH];
1993 char ed1[50], ed2[50];
1994 bool first=true;
1995 uint32_t maxpart=0, part;
1996 uint64_t maxpart_size=0;
1997 memset((void *)&pr, 0, sizeof(pr));
1998 memset((void *)&mr, 0, sizeof(mr));
1999
2000 /* Look at arguments */
2001 for (int i=1; i<ua->argc; i++) {
2002 if (strcasecmp(ua->argk[i], NT_("volume")) == 0
2003 && is_name_valid(ua->argv[i], NULL)) {
2004 bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
2005
2006 } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) {
2007 drive = atoi(ua->argv[i]);
2008 }
2009 }
2010
2011 if (!open_client_db(ua)) {
2012 goto bail_out;
2013 }
2014
2015 /* Choose storage */
2016 ua->jcr->wstore = store = get_storage_resource(ua, false);
2017 if (!store) {
2018 goto bail_out;
2019 }
2020 bstrncpy(storage, store->dev_name(), sizeof(storage));
2021 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
2022
2023 if ((sd=open_sd_bsock(ua)) == NULL) {
2024 Dmsg0(100, "Can't open connection to SD\n");
2025 goto bail_out;
2026 }
2027
2028 /* Protect us from spaces */
2029 bash_spaces(mr.MediaType);
2030 bash_spaces(storage);
2031 bash_spaces(mr.VolumeName);
2032
2033 sd->fsend("cloudlist Storage=%s Volume=%s MediaType=%s Slot=%d drive=%d\n",
2034 storage, mr.VolumeName, mr.MediaType, mr.Slot, drive);
2035
2036 if (mr.VolumeName[0]) { /* Want to list parts */
2037 const char *output_hformat="| %8d | %12sB | %20s |\n";
2038 uint64_t volsize=0;
2039 /* Check for valid response */
2040 while (sd->recv() >= 0) {
2041 if (sscanf(sd->msg, "part=%d size=%lld mtime=%lld", &part, &size, &mtime) != 3) {
2042 if (sd->msg[0] == '3') {
2043 ua->send_msg("%s", sd->msg);
2044 }
2045 continue;
2046 }
2047 /* Print information */
2048 if (first) {
2049 ua->send_msg(_("+----------+---------------+----------------------+\n"));
2050 ua->send_msg(_("| Part | Size | MTime |\n"));
2051 ua->send_msg(_("+----------+---------------+----------------------+\n"));
2052 first=false;
2053 }
2054 if (part > maxpart) {
2055 maxpart = part;
2056 maxpart_size = size;
2057 }
2058 volsize += size;
2059 ua->send_msg(output_hformat, part, edit_uint64_with_suffix(size, ed1), bstrftimes(ed2, sizeof(ed2), mtime));
2060 }
2061 if (!first) {
2062 ua->send_msg(_("+----------+---------------+----------------------+\n"));
2063 }
2064 /* TODO: See if we fix the catalog record directly */
2065 if (db_get_media_record(ua->jcr, ua->db, &mr)) {
2066 POOL_MEM errmsg, tmpmsg;
2067 if (mr.LastPartBytes != maxpart_size) {
2068 Mmsg(tmpmsg, "Error on volume \"%s\". Catalog LastPartBytes mismatch %lld != %lld\n",
2069 mr.VolumeName, mr.LastPartBytes, maxpart_size);
2070 pm_strcpy(errmsg, tmpmsg.c_str());
2071 }
2072 if (mr.VolCloudParts != maxpart) {
2073 Mmsg(tmpmsg, "Error on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n",
2074 mr.VolumeName, mr.VolCloudParts, maxpart);
2075 pm_strcpy(errmsg, tmpmsg.c_str());
2076 }
2077 if (strlen(errmsg.c_str()) > 0) {
2078 ua->error_msg("\n%s", errmsg.c_str());
2079 }
2080 }
2081 } else { /* TODO: Get the last part if possible? */
2082 const char *output_hformat="| %18s | %9s | %20s | %20s | %12sB |\n";
2083
2084 /* Check for valid response */
2085 while (sd->recv() >= 0) {
2086 if (sscanf(sd->msg, "volume=%127s", mr.VolumeName) != 1) {
2087 if (sd->msg[0] == '3') {
2088 ua->send_msg("%s", sd->msg);
2089 }
2090 continue;
2091 }
2092 unbash_spaces(mr.VolumeName);
2093
2094 mr.MediaId = 0;
2095
2096 if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
2097 memset(&pr, 0, sizeof(POOL_DBR));
2098 pr.PoolId = mr.PoolId;
2099 if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
2100 strcpy(pr.Name, "?");
2101 }
2102
2103 if (first) {
2104 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2105 ua->send_msg(_("| Volume Name | Status | Media Type | Pool | VolBytes |\n"));
2106 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2107 first=false;
2108 }
2109 /* Print information */
2110 ua->send_msg(output_hformat, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name,
2111 edit_uint64_with_suffix(mr.VolBytes, ed1));
2112 }
2113 }
2114 if (!first) {
2115 ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2116 }
2117 }
2118
2119 bail_out:
2120 close_db(ua);
2121 close_sd_bsock(ua);
2122 ua->jcr->wstore = NULL;
2123 return 1;
2124 }
2125
2126 /* Ask client to create/prune/delete a snapshot via the command line */
cloud_cmd(UAContext * ua,const char * cmd)2127 static int cloud_cmd(UAContext *ua, const char *cmd)
2128 {
2129 for (int i=0; i<ua->argc; i++) {
2130 if (strcasecmp(ua->argk[i], NT_("upload")) == 0) {
2131 return cloud_volumes_cmd(ua, cmd, "upload");
2132
2133 } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
2134 return cloud_list_cmd(ua, cmd);
2135
2136 } else if (strcasecmp(ua->argk[i], NT_("truncate")) == 0) {
2137 return cloud_volumes_cmd(ua, cmd, "truncate cache");
2138
2139 } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
2140
2141 } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
2142 return cloud_volumes_cmd(ua, cmd, "prunecache");
2143
2144 } else {
2145 continue;
2146 }
2147 }
2148
2149 for ( ;; ) {
2150
2151 start_prompt(ua, _("Cloud choice: \n"));
2152 add_prompt(ua, _("List Cloud Volumes in the Cloud"));
2153 add_prompt(ua, _("Upload a Volume to the Cloud"));
2154 add_prompt(ua, _("Prune the Cloud Cache"));
2155 add_prompt(ua, _("Truncate a Volume Cache"));
2156 add_prompt(ua, _("Done"));
2157
2158 switch(do_prompt(ua, "", _("Select action to perform on Cloud"), NULL, 0)) {
2159 case 0: /* list cloud */
2160 cloud_list_cmd(ua, cmd);
2161 break;
2162 case 1: /* upload */
2163 cloud_volumes_cmd(ua, cmd, "upload");
2164 break;
2165 case 2: /* Prune cache */
2166 cloud_volumes_cmd(ua, cmd, "prunecache");
2167 break;
2168 case 3: /* Truncate cache */
2169 cloud_volumes_cmd(ua, cmd, "truncate cache");
2170 break;
2171 default:
2172 ua->info_msg(_("Selection terminated.\n"));
2173 return 1;
2174 }
2175 }
2176 return 1;
2177 }
2178
2179 /*
2180 * Switch databases
2181 * use catalog=<name>
2182 */
use_cmd(UAContext * ua,const char * cmd)2183 static int use_cmd(UAContext *ua, const char *cmd)
2184 {
2185 CAT *oldcatalog, *catalog;
2186
2187
2188 close_db(ua); /* close any previously open db */
2189 oldcatalog = ua->catalog;
2190
2191 if (!(catalog = get_catalog_resource(ua))) {
2192 ua->catalog = oldcatalog;
2193 } else {
2194 ua->catalog = catalog;
2195 }
2196 if (open_db(ua)) {
2197 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
2198 ua->catalog->name(), ua->catalog->db_name);
2199 }
2200 return 1;
2201 }
2202
quit_cmd(UAContext * ua,const char * cmd)2203 int quit_cmd(UAContext *ua, const char *cmd)
2204 {
2205 ua->quit = true;
2206 return 1;
2207 }
2208
2209 /* Handler to get job status */
status_handler(void * ctx,int num_fields,char ** row)2210 static int status_handler(void *ctx, int num_fields, char **row)
2211 {
2212 char *val = (char *)ctx;
2213
2214 if (row[0]) {
2215 *val = row[0][0];
2216 } else {
2217 *val = '?'; /* Unknown by default */
2218 }
2219
2220 return 0;
2221 }
2222
2223 /*
2224 * Wait until no job is running
2225 */
wait_cmd(UAContext * ua,const char * cmd)2226 int wait_cmd(UAContext *ua, const char *cmd)
2227 {
2228 JCR *jcr;
2229 int i;
2230 time_t stop_time = 0;
2231
2232 /*
2233 * no args
2234 * Wait until no job is running
2235 */
2236 if (ua->argc == 1) {
2237 bmicrosleep(0, 200000); /* let job actually start */
2238 for (bool running=true; running; ) {
2239 running = false;
2240 foreach_jcr(jcr) {
2241 if (!jcr->is_internal_job()) {
2242 running = true;
2243 break;
2244 }
2245 }
2246 endeach_jcr(jcr);
2247
2248 if (running) {
2249 bmicrosleep(1, 0);
2250 }
2251 }
2252 return 1;
2253 }
2254
2255 i = find_arg_with_value(ua, NT_("timeout"));
2256 if (i > 0 && ua->argv[i]) {
2257 stop_time = time(NULL) + str_to_int64(ua->argv[i]);
2258 }
2259
2260 /* we have jobid, jobname or ujobid argument */
2261
2262 uint32_t jobid = 0 ;
2263
2264 if (!open_client_db(ua)) {
2265 ua->error_msg(_("ERR: Can't open db\n")) ;
2266 return 1;
2267 }
2268
2269 for (int i=1; i<ua->argc; i++) {
2270 if (strcasecmp(ua->argk[i], "jobid") == 0) {
2271 if (!ua->argv[i]) {
2272 break;
2273 }
2274 jobid = str_to_int64(ua->argv[i]);
2275 break;
2276 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
2277 strcasecmp(ua->argk[i], "job") == 0) {
2278 if (!ua->argv[i]) {
2279 break;
2280 }
2281 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
2282 if (jcr) {
2283 jobid = jcr->JobId ;
2284 free_jcr(jcr);
2285 }
2286 break;
2287 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
2288 if (!ua->argv[i]) {
2289 break;
2290 }
2291 jcr=get_jcr_by_full_name(ua->argv[i]) ;
2292 if (jcr) {
2293 jobid = jcr->JobId ;
2294 free_jcr(jcr);
2295 }
2296 break;
2297 /* Wait for a mount request */
2298 } else if (strcasecmp(ua->argk[i], "mount") == 0) {
2299 for (bool waiting=false; !waiting; ) {
2300 foreach_jcr(jcr) {
2301 if (!jcr->is_internal_job() &&
2302 (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount ||
2303 jcr->SDJobStatus == JS_WaitMedia || jcr->SDJobStatus == JS_WaitMount))
2304 {
2305 waiting = true;
2306 break;
2307 }
2308 }
2309 endeach_jcr(jcr);
2310 if (waiting) {
2311 break;
2312 }
2313 if (stop_time && (time(NULL) >= stop_time)) {
2314 ua->warning_msg(_("Wait on mount timed out\n"));
2315 return 1;
2316 }
2317 bmicrosleep(1, 0);
2318 }
2319 return 1;
2320 }
2321 }
2322
2323 if (jobid == 0) {
2324 ua->error_msg(_("ERR: Job was not found\n"));
2325 return 1 ;
2326 }
2327
2328 /*
2329 * We wait the end of a specific job
2330 */
2331
2332 bmicrosleep(0, 200000); /* let job actually start */
2333 for (bool running=true; running; ) {
2334 running = false;
2335
2336 jcr=get_jcr_by_id(jobid) ;
2337
2338 if (jcr) {
2339 running = true ;
2340 free_jcr(jcr);
2341 }
2342
2343 if (running) {
2344 bmicrosleep(1, 0);
2345 }
2346 }
2347
2348 /*
2349 * We have to get JobStatus
2350 */
2351
2352 int status ;
2353 char jobstatus = '?'; /* Unknown by default */
2354 char buf[256] ;
2355
2356 bsnprintf(buf, sizeof(buf),
2357 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
2358
2359
2360 db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
2361
2362 switch (jobstatus) {
2363 case JS_Error:
2364 status = 1 ; /* Warning */
2365 break;
2366
2367 case JS_Incomplete:
2368 case JS_FatalError:
2369 case JS_ErrorTerminated:
2370 case JS_Canceled:
2371 status = 2 ; /* Critical */
2372 break;
2373
2374 case JS_Warnings:
2375 case JS_Terminated:
2376 status = 0 ; /* Ok */
2377 break;
2378
2379 default:
2380 status = 3 ; /* Unknown */
2381 break;
2382 }
2383
2384 ua->send_msg("JobId=%i\n", jobid) ;
2385 ua->send_msg("JobStatus=%s (%c)\n",
2386 job_status_to_str(jobstatus, 0),
2387 jobstatus) ;
2388
2389 if (ua->gui || ua->api) {
2390 ua->send_msg("ExitStatus=%i\n", status) ;
2391 }
2392
2393 return 1;
2394 }
2395
2396
help_cmd(UAContext * ua,const char * cmd)2397 static int help_cmd(UAContext *ua, const char *cmd)
2398 {
2399 int i;
2400 ua->send_msg(_(" Command Description\n ======= ===========\n"));
2401 for (i=0; i<comsize; i++) {
2402 if (ua->argc == 2) {
2403 if (!strcasecmp(ua->argk[1], commands[i].key)) {
2404 ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2405 commands[i].help, commands[i].usage);
2406 break;
2407 }
2408 } else {
2409 ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help);
2410 }
2411 }
2412 if (i == comsize && ua->argc == 2) {
2413 ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2414 }
2415 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2416 return 1;
2417 }
2418
qhelp_cmd(UAContext * ua,const char * cmd)2419 int qhelp_cmd(UAContext *ua, const char *cmd)
2420 {
2421 int i,j;
2422 /* Want to display only commands */
2423 j = find_arg(ua, NT_("all"));
2424 if (j >= 0) {
2425 for (i=0; i<comsize; i++) {
2426 ua->send_msg("%s\n", commands[i].key);
2427 }
2428 return 1;
2429 }
2430 /* Want to display a specific help section */
2431 j = find_arg_with_value(ua, NT_("item"));
2432 if (j >= 0 && ua->argk[j]) {
2433 for (i=0; i<comsize; i++) {
2434 if (bstrcmp(commands[i].key, ua->argv[j])) {
2435 ua->send_msg("%s\n", commands[i].usage);
2436 break;
2437 }
2438 }
2439 return 1;
2440 }
2441 /* Want to display everything */
2442 for (i=0; i<comsize; i++) {
2443 ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2444 }
2445 return 1;
2446 }
2447
2448 #if 1
version_cmd(UAContext * ua,const char * cmd)2449 static int version_cmd(UAContext *ua, const char *cmd)
2450 {
2451 ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2452 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2453 return 1;
2454 }
2455 #else
2456 /*
2457 * Test code -- turned on only for debug testing
2458 */
version_cmd(UAContext * ua,const char * cmd)2459 static int version_cmd(UAContext *ua, const char *cmd)
2460 {
2461 dbid_list ids;
2462 POOL_MEM query(PM_MESSAGE);
2463 open_db(ua);
2464 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2465 db_get_query_dbids(ua->jcr, ua->db, query, ids);
2466 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2467 for (int i=0; i < ids.num_ids; i++) {
2468 ua->send_msg("id=%d\n", ids.DBId[i]);
2469 }
2470 close_db(ua);
2471 return 1;
2472 }
2473 #endif
2474
2475 /*
2476 * This call uses open_client_db() and force a
2477 * new dedicated connection to the catalog
2478 */
open_new_client_db(UAContext * ua)2479 bool open_new_client_db(UAContext *ua)
2480 {
2481 bool ret;
2482
2483 /* Force a new dedicated connection */
2484 ua->force_mult_db_connections = true;
2485 ret = open_client_db(ua);
2486 ua->force_mult_db_connections = false;
2487
2488 return ret;
2489 }
2490
2491 /*
2492 * This call explicitly checks for a catalog=xxx and
2493 * if given, opens that catalog. It also checks for
2494 * client=xxx and if found, opens the catalog
2495 * corresponding to that client. If we still don't
2496 * have a catalog, look for a Job keyword and get the
2497 * catalog from its client record.
2498 */
open_client_db(UAContext * ua)2499 bool open_client_db(UAContext *ua)
2500 {
2501 int i;
2502 CAT *catalog;
2503 CLIENT *client;
2504 JOB *job;
2505
2506 /* Try for catalog keyword */
2507 i = find_arg_with_value(ua, NT_("catalog"));
2508 if (i >= 0) {
2509 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2510 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2511 return false;
2512 }
2513 catalog = GetCatalogResWithName(ua->argv[i]);
2514 if (catalog) {
2515 if (ua->catalog && ua->catalog != catalog) {
2516 close_db(ua);
2517 }
2518 ua->catalog = catalog;
2519 return open_db(ua);
2520 }
2521 }
2522
2523 /* Try for client keyword */
2524 i = find_arg_with_value(ua, NT_("client"));
2525 if (i >= 0) {
2526 if (!acl_access_client_ok(ua, ua->argv[i], JT_BACKUP_RESTORE)) {
2527 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2528 return false;
2529 }
2530 client = GetClientResWithName(ua->argv[i]);
2531 if (client) {
2532 catalog = client->catalog;
2533 if (ua->catalog && ua->catalog != catalog) {
2534 close_db(ua);
2535 }
2536 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2537 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2538 return false;
2539 }
2540 ua->catalog = catalog;
2541 return open_db(ua);
2542 }
2543 }
2544
2545 /* Try for Job keyword */
2546 i = find_arg_with_value(ua, NT_("job"));
2547 if (i >= 0) {
2548 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2549 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2550 return false;
2551 }
2552 job = GetJobResWithName(ua->argv[i]);
2553 if (job) {
2554 catalog = job->client->catalog;
2555 if (ua->catalog && ua->catalog != catalog) {
2556 close_db(ua);
2557 }
2558 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2559 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2560 return false;
2561 }
2562 ua->catalog = catalog;
2563 return open_db(ua);
2564 }
2565 }
2566
2567 return open_db(ua);
2568 }
2569
2570
2571 /*
2572 * Open the catalog database.
2573 */
open_db(UAContext * ua)2574 bool open_db(UAContext *ua)
2575 {
2576 bool mult_db_conn;
2577
2578 /* With a restricted console, we can't share a SQL connection */
2579 if (ua->cons) {
2580 ua->force_mult_db_connections = true;
2581 }
2582
2583 /* The force_mult_db_connections is telling us if we modify the
2584 * private or the shared link
2585 */
2586 if (ua->force_mult_db_connections) {
2587 ua->db = ua->private_db;
2588
2589 } else {
2590 ua->db = ua->shared_db;
2591 }
2592
2593 if (ua->db) {
2594 return true;
2595 }
2596
2597 if (!ua->catalog) {
2598 ua->catalog = get_catalog_resource(ua);
2599 if (!ua->catalog) {
2600 ua->error_msg( _("Could not find a Catalog resource\n"));
2601 return false;
2602 }
2603 }
2604
2605 /* Some modules like bvfs need their own catalog connection */
2606 mult_db_conn = ua->catalog->mult_db_connections;
2607 if (ua->force_mult_db_connections) {
2608 mult_db_conn = true;
2609 }
2610
2611 ua->jcr->catalog = ua->catalog;
2612
2613 Dmsg0(100, "UA Open database\n");
2614 ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2615 ua->catalog->db_name,
2616 ua->catalog->db_user,
2617 ua->catalog->db_password, ua->catalog->db_address,
2618 ua->catalog->db_port, ua->catalog->db_socket,
2619 ua->catalog->db_ssl_mode, ua->catalog->db_ssl_key,
2620 ua->catalog->db_ssl_cert, ua->catalog->db_ssl_ca,
2621 ua->catalog->db_ssl_capath, ua->catalog->db_ssl_cipher,
2622 mult_db_conn, ua->catalog->disable_batch_insert);
2623 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2624 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2625 ua->catalog->db_name);
2626 if (ua->db) {
2627 ua->error_msg("%s", db_strerror(ua->db));
2628 }
2629 close_db(ua);
2630 return false;
2631 }
2632 ua->jcr->db = ua->db;
2633
2634 /* Depending on the type of connection, we set the right variable */
2635 if (ua->force_mult_db_connections) {
2636 ua->private_db = ua->db;
2637
2638 } else {
2639 ua->shared_db = ua->db;
2640 }
2641 /* With a restricted console, the DB backend should know restrictions about
2642 * Pool, Job, etc...
2643 */
2644 if (ua->cons) {
2645 ua->db->set_acl(ua->jcr, DB_ACL_JOB, ua->cons->ACL_lists[Job_ACL]);
2646 ua->db->set_acl(ua->jcr, DB_ACL_CLIENT, ua->cons->ACL_lists[Client_ACL]);
2647 ua->db->set_acl(ua->jcr, DB_ACL_POOL, ua->cons->ACL_lists[Pool_ACL]);
2648 ua->db->set_acl(ua->jcr, DB_ACL_FILESET, ua->cons->ACL_lists[FileSet_ACL]);
2649
2650 /* For RestoreClient and BackupClient, we take also in account the Client list */
2651 ua->db->set_acl(ua->jcr, DB_ACL_RCLIENT,
2652 ua->cons->ACL_lists[Client_ACL],
2653 ua->cons->ACL_lists[RestoreClient_ACL]);
2654
2655 ua->db->set_acl(ua->jcr, DB_ACL_BCLIENT,
2656 ua->cons->ACL_lists[Client_ACL],
2657 ua->cons->ACL_lists[BackupClient_ACL]);
2658 }
2659 if (!ua->api) {
2660 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2661 }
2662 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2663 return true;
2664 }
2665
close_db(UAContext * ua)2666 void close_db(UAContext *ua)
2667 {
2668 if (ua->jcr) {
2669 ua->jcr->db = NULL;
2670 }
2671
2672 if (ua->shared_db) {
2673 db_close_database(ua->jcr, ua->shared_db);
2674 ua->shared_db = NULL;
2675 }
2676
2677 if (ua->private_db) {
2678 db_close_database(ua->jcr, ua->private_db);
2679 ua->private_db = NULL;
2680 }
2681
2682 ua->db = NULL;
2683 }
2684