]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Add .bvfs_get_jobids to return jobids needed by a job to restore
[bacula/bacula] / bacula / src / dird / ua_dotcmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
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    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    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Commands
31  *     These are "dot" commands, i.e. commands preceded
32  *        by a period. These commands are meant to be used
33  *        by a program, so there is no prompting, and the
34  *        returned results are (supposed to be) predictable.
35  *
36  *     Kern Sibbald, April MMII
37  *
38  */
39
40 #include "bacula.h"
41 #include "dird.h"
42 #include "cats/bvfs.h"
43 #include "findlib/find.h"
44
45 /* Imported variables */
46
47 /* Imported functions */
48 extern void do_messages(UAContext *ua, const char *cmd);
49 extern int quit_cmd(UAContext *ua, const char *cmd);
50 extern int qhelp_cmd(UAContext *ua, const char *cmd);
51 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
52
53
54 /* Forward referenced functions */
55 static bool admin_cmds(UAContext *ua, const char *cmd);
56 static bool jobscmd(UAContext *ua, const char *cmd);
57 static bool filesetscmd(UAContext *ua, const char *cmd);
58 static bool clientscmd(UAContext *ua, const char *cmd);
59 static bool msgscmd(UAContext *ua, const char *cmd);
60 static bool poolscmd(UAContext *ua, const char *cmd);
61 static bool storagecmd(UAContext *ua, const char *cmd);
62 static bool defaultscmd(UAContext *ua, const char *cmd);
63 static bool typescmd(UAContext *ua, const char *cmd);
64 static bool backupscmd(UAContext *ua, const char *cmd);
65 static bool levelscmd(UAContext *ua, const char *cmd);
66 static bool getmsgscmd(UAContext *ua, const char *cmd);
67 static bool volstatuscmd(UAContext *ua, const char *cmd);
68 static bool mediatypescmd(UAContext *ua, const char *cmd);
69 static bool locationscmd(UAContext *ua, const char *cmd);
70 static bool mediacmd(UAContext *ua, const char *cmd);
71 static bool aopcmd(UAContext *ua, const char *cmd);
72
73 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd);
74 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd);
75 static bool dot_bvfs_update(UAContext *ua, const char *cmd);
76 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd);
77
78 static bool api_cmd(UAContext *ua, const char *cmd);
79 static bool sql_cmd(UAContext *ua, const char *cmd);
80 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
81 static bool dot_help_cmd(UAContext *ua, const char *cmd);
82
83 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
84 static struct cmdstruct commands[] = { /* help */  /* can be used in runscript */
85  { NT_(".api"),        api_cmd,          NULL,       false},
86  { NT_(".backups"),    backupscmd,       NULL,       false},
87  { NT_(".clients"),    clientscmd,       NULL,       true},
88  { NT_(".defaults"),   defaultscmd,      NULL,       false},
89  { NT_(".die"),        admin_cmds,       NULL,       false},
90  { NT_(".dump"),       admin_cmds,       NULL,       false},
91  { NT_(".exit"),       admin_cmds,       NULL,       false},
92  { NT_(".filesets"),   filesetscmd,      NULL,       false},
93  { NT_(".help"),       dot_help_cmd,     NULL,       false},
94  { NT_(".jobs"),       jobscmd,          NULL,       true},
95  { NT_(".levels"),     levelscmd,        NULL,       false},
96  { NT_(".messages"),   getmsgscmd,       NULL,       false},
97  { NT_(".msgs"),       msgscmd,          NULL,       false},
98  { NT_(".pools"),      poolscmd,         NULL,       true},
99  { NT_(".quit"),       dot_quit_cmd,     NULL,       false},
100  { NT_(".sql"),        sql_cmd,          NULL,       false},
101  { NT_(".status"),     dot_status_cmd,   NULL,       false},
102  { NT_(".storage"),    storagecmd,       NULL,       true},
103  { NT_(".volstatus"),  volstatuscmd,     NULL,       true},
104  { NT_(".media"),      mediacmd,         NULL,       true},
105  { NT_(".mediatypes"), mediatypescmd,    NULL,       true},
106  { NT_(".locations"),  locationscmd,     NULL,       true},
107  { NT_(".actiononpurge"),aopcmd,         NULL,       true},
108  { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs, NULL,       true},
109  { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles,NULL,       true},
110  { NT_(".bvfs_update"), dot_bvfs_update, NULL,       true},
111  { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids,NULL,true},
112  { NT_(".types"),      typescmd,         NULL,       false}
113              };
114 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
115
116 /*
117  * Execute a command from the UA
118  */
119 bool do_a_dot_command(UAContext *ua) 
120 {
121    int i;
122    int len;
123    bool ok = false;
124    bool found = false;
125    BSOCK *user = ua->UA_sock;
126
127    Dmsg1(1400, "Dot command: %s\n", user->msg);
128    if (ua->argc == 0) {
129       return false;
130    }
131
132    len = strlen(ua->argk[0]);
133    if (len == 1) {
134       if (ua->api) user->signal(BNET_CMD_BEGIN);
135       if (ua->api) user->signal(BNET_CMD_OK);
136       return true;                    /* no op */
137    }
138    for (i=0; i<comsize; i++) {     /* search for command */
139       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
140          /* Check if this command is authorized in RunScript */
141          if (ua->runscript && !commands[i].use_in_rs) {
142             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
143             break;
144          }
145          bool gui = ua->gui;
146          /* Check if command permitted, but "quit" is always OK */
147          if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
148              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
149             break;
150          }
151          Dmsg1(100, "Cmd: %s\n", ua->cmd);
152          ua->gui = true;
153          if (ua->api) user->signal(BNET_CMD_BEGIN);
154          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
155          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
156          ua->gui = gui;
157          found = true;
158          break;
159       }
160    }
161    if (!found) {
162       pm_strcat(user->msg, _(": is an invalid command.\n"));
163       ua->error_msg("%s", user->msg);
164       ok = false;
165    }
166    return ok;
167 }
168
169 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
170 {
171    if (!open_new_client_db(ua)) {
172       return 1;
173    }
174
175    int pos = find_arg_with_value(ua, "jobid");
176    if (pos != -1 && is_a_number_list(ua->argv[pos])) {
177       POOL_MEM jobids;
178       pm_strcpy(jobids, ua->argv[pos]);
179       bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, jobids.c_str());
180    } else {
181       /* update cache for all jobids */
182       bvfs_update_cache(ua->jcr, ua->db);
183    }
184    
185    close_db(ua);
186    return true;
187 }
188
189 static int bvfs_result_handler(void *ctx, int fields, char **row)
190 {
191    UAContext *ua = (UAContext *)ctx;
192    struct stat statp;
193    int32_t LinkFI;
194    char *fileid=row[BVFS_FileId];
195    char *lstat=row[BVFS_LStat];
196    char *jobid=row[BVFS_JobId];
197    
198    char empty[] = "A A A A A A A A A A A A A A";
199    char zero[] = "0";
200
201    /* We need to deal with non existant path */
202    if (!fileid || !is_a_number(fileid)) {
203       lstat = empty;
204       jobid = zero;
205       fileid = zero;
206    }
207
208    memset(&statp, 0, sizeof(struct stat));
209    decode_stat(lstat, &statp, &LinkFI);
210
211    Dmsg1(100, "type=%s\n", row[0]);
212    if (bvfs_is_dir(row)) {
213       char *path = bvfs_basename_dir(row[BVFS_Name]);
214       ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
215                    jobid, lstat, path);
216
217    } else if (bvfs_is_file(row)) {
218       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
219                    row[BVFS_FilenameId], fileid, jobid,
220                    lstat, row[BVFS_Name]);
221    }
222
223    return 0;
224 }
225
226 static bool bvfs_parse_arg(UAContext *ua, 
227                            DBId_t *pathid, char **path, char **jobid,
228                            int *limit, int *offset)
229 {
230    *pathid=0;
231    *limit=2000;
232    *offset=0;
233    *path=NULL;
234    *jobid=NULL;
235
236    for (int i=1; i<ua->argc; i++) {
237       if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
238          if (is_a_number(ua->argv[i])) {
239             *pathid = str_to_int64(ua->argv[i]);
240          }
241       }
242       if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
243          *path = ua->argv[i];
244       }
245       
246       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
247          if (is_a_number_list(ua->argv[i])) {
248             *jobid = ua->argv[i];
249          }
250       }
251
252       if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
253          if (is_a_number(ua->argv[i])) {
254             *limit = str_to_int64(ua->argv[i]);
255          }
256       }
257
258       if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
259          if (is_a_number(ua->argv[i])) {
260             *offset = str_to_int64(ua->argv[i]);
261          }
262       }
263    }
264
265    if (!((*pathid || *path) && *jobid)) {
266       return false;
267    }
268
269    if (!open_client_db(ua)) {
270       return false;
271    }
272
273    return true;
274 }
275
276 /* 
277  * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
278  * .bvfs_lsfiles jobid=1,2,3,4 path=/
279  */
280 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
281 {
282    DBId_t pathid=0;
283    int limit=2000, offset=0;
284    char *path=NULL, *jobid=NULL;
285
286    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
287                        &limit, &offset))
288    {
289       ua->error_msg("Can't find jobid, pathid or path argument\n");
290       return true;              /* not enough param */
291    }
292
293    Bvfs fs(ua->jcr, ua->db);
294    fs.set_jobids(jobid);   
295    fs.set_handler(bvfs_result_handler, ua);
296    fs.set_limit(limit);
297
298    if (pathid) {
299       fs.ch_dir(pathid);
300    } else {
301       fs.ch_dir(path);
302    }
303
304    fs.set_offset(offset);
305
306    fs.ls_files();
307
308    return true;
309 }
310
311 /* 
312  * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
313  * .bvfs_lsdirs jobid=1,2,3,4 path=/
314  * .bvfs_lsdirs jobid=1,2,3,4 path=
315  */
316 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
317 {
318    DBId_t pathid=0;
319    int limit=2000, offset=0;
320    char *path=NULL, *jobid=NULL;
321
322    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
323                        &limit, &offset))
324    {
325       ua->error_msg("Can't find jobid, pathid or path argument\n");
326       return true;              /* not enough param */
327    }
328
329    Bvfs fs(ua->jcr, ua->db);
330    fs.set_jobids(jobid);   
331    fs.set_limit(limit);
332    fs.set_handler(bvfs_result_handler, ua);
333
334    if (pathid) {
335       fs.ch_dir(pathid);
336    } else {
337       fs.ch_dir(path);
338    }
339
340    fs.set_offset(offset);
341
342    fs.ls_special_dirs();
343    fs.ls_dirs();
344
345    return true;
346 }
347
348 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
349 {
350    JOB_DBR jr;
351    db_list_ctx jobids;
352    int pos;
353
354    if (!open_client_db(ua)) {
355       return true;
356    }
357
358    memset(&jr, 0, sizeof(JOB_DBR));
359    if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
360       jr.JobId = str_to_int64(ua->argv[pos]);
361    }
362
363    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
364       ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
365                     ua->cmd, db_strerror(ua->db));
366       return true;
367    }
368    jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
369    if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) {
370       return true;
371    }
372    ua->send_msg("%s\n", jobids.list);
373    return true;
374 }
375
376 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
377 {
378    quit_cmd(ua, cmd);
379    return true;
380 }
381
382 static bool dot_help_cmd(UAContext *ua, const char *cmd)
383 {
384    qhelp_cmd(ua, cmd);
385    return true;
386 }
387
388 static bool getmsgscmd(UAContext *ua, const char *cmd)
389 {
390    if (console_msg_pending) {
391       do_messages(ua, cmd);
392    }
393    return 1;
394 }
395
396 #ifdef DEVELOPER
397 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
398 {
399    BSOCK *sd;
400    JCR *jcr = ua->jcr;
401    USTORE lstore;
402    
403    lstore.store = store;
404    pm_strcpy(lstore.store_source, _("unknown source"));
405    set_wstorage(jcr, &lstore);
406    /* Try connecting for up to 15 seconds */
407    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
408       store->name(), store->address, store->SDport);
409    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
410       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
411       return;
412    }
413    Dmsg0(120, _("Connected to storage daemon\n"));
414    sd = jcr->store_bsock;
415    sd->fsend("%s", cmd);
416    if (sd->recv() >= 0) {
417       ua->send_msg("%s", sd->msg);
418    }
419    sd->signal(BNET_TERMINATE);
420    sd->close();
421    jcr->store_bsock = NULL;
422    return;
423 }
424
425 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
426 {
427    BSOCK *fd;
428
429    /* Connect to File daemon */
430
431    ua->jcr->client = client;
432    /* Try to connect for 15 seconds */
433    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
434       client->name(), client->address, client->FDport);
435    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
436       ua->error_msg(_("Failed to connect to Client.\n"));
437       return;
438    }
439    Dmsg0(120, "Connected to file daemon\n");
440    fd = ua->jcr->file_bsock;
441    fd->fsend("%s", cmd);
442    if (fd->recv() >= 0) {
443       ua->send_msg("%s", fd->msg);
444    }
445    fd->signal(BNET_TERMINATE);
446    fd->close();
447    ua->jcr->file_bsock = NULL;
448    return;
449 }
450
451 /*
452  *   .die (seg fault)
453  *   .dump (sm_dump)
454  *   .exit (no arg => .quit)
455  */
456 static bool admin_cmds(UAContext *ua, const char *cmd)
457 {
458    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
459    STORE *store=NULL;
460    CLIENT *client=NULL;
461    bool dir=false;
462    bool do_deadlock=false;
463    const char *remote_cmd;
464    int i;
465    JCR *jcr = NULL;
466    int a;
467    if (strncmp(ua->argk[0], ".die", 4) == 0) {
468       if (find_arg(ua, "deadlock") > 0) {
469          do_deadlock = true;
470          remote_cmd = ".die deadlock";
471       } else {
472          remote_cmd = ".die";
473       }
474    } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
475       remote_cmd = "sm_dump";
476    } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
477       remote_cmd = "exit";
478    } else {
479       ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
480       return true;
481    }
482    /* General debug? */
483    for (i=1; i<ua->argc; i++) {
484       if (strcasecmp(ua->argk[i], "dir") == 0 ||
485           strcasecmp(ua->argk[i], "director") == 0) {
486          dir = true;
487       }
488       if (strcasecmp(ua->argk[i], "client") == 0 ||
489           strcasecmp(ua->argk[i], "fd") == 0) {
490          client = NULL;
491          if (ua->argv[i]) {
492             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
493          }
494          if (!client) {
495             client = select_client_resource(ua);
496          }
497       }
498    
499       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
500           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
501           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
502          store = NULL;
503          if (ua->argv[i]) {
504             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
505          }
506          if (!store) {
507             store = get_storage_resource(ua, false/*no default*/);
508          }
509       }
510    }
511
512    if (!dir && !store && !client) {
513       /*
514        * We didn't find an appropriate keyword above, so
515        * prompt the user.
516        */
517       start_prompt(ua, _("Available daemons are: \n"));
518       add_prompt(ua, _("Director"));
519       add_prompt(ua, _("Storage"));
520       add_prompt(ua, _("Client"));
521       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
522       case 0:                         /* Director */
523          dir=true;
524          break;
525       case 1:
526          store = get_storage_resource(ua, false/*no default*/);
527          break;
528       case 2:
529          client = select_client_resource(ua);
530          break;
531       default:
532          break;
533       }
534    }
535
536    if (store) {
537       do_storage_cmd(ua, store, remote_cmd);
538    }
539
540    if (client) {
541       do_client_cmd(ua, client, remote_cmd);
542    }
543
544    if (dir) {
545       if (strncmp(remote_cmd, ".die", 4) == 0) {
546          if (do_deadlock) {
547             ua->send_msg(_("The Director will generate a deadlock.\n"));
548             P(mutex); 
549             P(mutex);
550          }
551          ua->send_msg(_("The Director will segment fault.\n"));
552          a = jcr->JobId; /* ref NULL pointer */
553          jcr->JobId = 1000; /* another ref NULL pointer */
554
555       } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
556          sm_dump(false, true);
557       } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
558          dot_quit_cmd(ua, cmd);
559       }
560    }
561
562    return true;
563 }
564
565 #else
566
567 /*
568  * Dummy routine for non-development version
569  */
570 static bool admin_cmds(UAContext *ua, const char *cmd)
571 {
572    ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
573    return true;
574 }
575
576 #endif
577
578 /* 
579  * Can use an argument to filter on JobType
580  * .jobs [type=B]
581  */
582 static bool jobscmd(UAContext *ua, const char *cmd)
583 {
584    JOB *job;
585    uint32_t type = 0;
586    int pos;
587    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
588       type = ua->argv[pos][0];
589    }
590    LockRes();
591    foreach_res(job, R_JOB) {
592       if (!type || type == job->JobType) {
593          if (acl_access_ok(ua, Job_ACL, job->name())) {
594             ua->send_msg("%s\n", job->name());
595          }
596       }
597    }
598    UnlockRes();
599    return true;
600 }
601
602 static bool filesetscmd(UAContext *ua, const char *cmd)
603 {
604    FILESET *fs;
605    LockRes();
606    foreach_res(fs, R_FILESET) {
607       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
608          ua->send_msg("%s\n", fs->name());
609       }
610    }
611    UnlockRes();
612    return true;
613 }
614
615 static bool clientscmd(UAContext *ua, const char *cmd)
616 {
617    CLIENT *client;       
618    LockRes();
619    foreach_res(client, R_CLIENT) {
620       if (acl_access_ok(ua, Client_ACL, client->name())) {
621          ua->send_msg("%s\n", client->name());
622       }
623    }
624    UnlockRes();
625    return true;
626 }
627
628 static bool msgscmd(UAContext *ua, const char *cmd)
629 {
630    MSGS *msgs = NULL;
631    LockRes();
632    foreach_res(msgs, R_MSGS) {
633       ua->send_msg("%s\n", msgs->name());
634    }
635    UnlockRes();
636    return true;
637 }
638
639 static bool poolscmd(UAContext *ua, const char *cmd)
640 {
641    POOL *pool;       
642    LockRes();
643    foreach_res(pool, R_POOL) {
644       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
645          ua->send_msg("%s\n", pool->name());
646       }
647    }
648    UnlockRes();
649    return true;
650 }
651
652 static bool storagecmd(UAContext *ua, const char *cmd)
653 {
654    STORE *store;
655    LockRes();
656    foreach_res(store, R_STORAGE) {
657       if (acl_access_ok(ua, Storage_ACL, store->name())) {
658          ua->send_msg("%s\n", store->name());
659       }
660    }
661    UnlockRes();
662    return true;
663 }
664
665 static bool aopcmd(UAContext *ua, const char *cmd)
666 {
667    ua->send_msg("None\n");
668    ua->send_msg("Truncate\n");
669    return true;
670 }
671
672 static bool typescmd(UAContext *ua, const char *cmd)
673 {
674    ua->send_msg("Backup\n");
675    ua->send_msg("Restore\n");
676    ua->send_msg("Admin\n");
677    ua->send_msg("Verify\n");
678    ua->send_msg("Migrate\n");
679    ua->send_msg("Copy\n");
680    return true;
681 }
682
683 /*
684  * If this command is called, it tells the director that we
685  *  are a program that wants a sort of API, and hence,
686  *  we will probably suppress certain output, include more
687  *  error codes, and most of all send back a good number
688  *  of new signals that indicate whether or not the command
689  *  succeeded.
690  */
691 static bool api_cmd(UAContext *ua, const char *cmd)
692 {
693    if (ua->argc == 2) {
694       ua->api = atoi(ua->argk[1]);
695    } else {
696       ua->api = 1;
697    }
698    return true;
699 }
700
701 static int client_backups_handler(void *ctx, int num_field, char **row)
702 {
703    UAContext *ua = (UAContext *)ctx;
704    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
705       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
706    return 0;
707 }
708
709 /*
710  * Return the backups for this client 
711  *
712  *  .backups client=xxx fileset=yyy
713  *
714  */
715 static bool backupscmd(UAContext *ua, const char *cmd)
716 {
717    if (!open_client_db(ua)) {
718       return true;
719    }
720    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 || 
721        strcmp(ua->argk[2], "fileset") != 0) {
722       return true;
723    }
724    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
725        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
726       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
727       return true;
728    }
729    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
730    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
731       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
732       return true;
733    }
734    return true;
735 }
736
737 static int sql_handler(void *ctx, int num_field, char **row)
738 {
739    UAContext *ua = (UAContext *)ctx;
740    POOL_MEM rows(PM_MESSAGE);
741
742    /* Check for nonsense */
743    if (num_field == 0 || row == NULL || row[0] == NULL) {
744       return 0;                       /* nothing returned */
745    }
746    for (int i=0; num_field--; i++) {
747       if (i == 0) {
748          pm_strcpy(rows, NPRT(row[0]));
749       } else {
750          pm_strcat(rows, NPRT(row[i]));
751       }
752       pm_strcat(rows, "\t");
753    }
754    if (!rows.c_str() || !*rows.c_str()) {
755       ua->send_msg("\t");
756    } else {
757       ua->send_msg("%s", rows.c_str());
758    }
759    return 0;
760 }
761
762 static bool sql_cmd(UAContext *ua, const char *cmd)
763 {
764    int index;
765    if (!open_client_db(ua)) {
766       return true;
767    }
768    index = find_arg_with_value(ua, "query");
769    if (index < 0) {
770       ua->error_msg(_("query keyword not found.\n"));
771       return true;
772    }
773    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
774       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
775       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
776       return true;
777    }
778    return true;
779 }
780       
781 static int one_handler(void *ctx, int num_field, char **row)
782 {
783    UAContext *ua = (UAContext *)ctx;
784    ua->send_msg("%s\n", row[0]);
785    return 0;
786 }
787
788 static bool mediatypescmd(UAContext *ua, const char *cmd)
789 {
790    if (!open_client_db(ua)) {
791       return true;
792    }
793    if (!db_sql_query(ua->db, 
794                   "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
795                   one_handler, (void *)ua)) 
796    {
797       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
798    }
799    return true;
800 }
801
802 static bool mediacmd(UAContext *ua, const char *cmd)
803 {
804    if (!open_client_db(ua)) {
805       return true;
806    }
807    if (!db_sql_query(ua->db, 
808                   "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
809                   one_handler, (void *)ua)) 
810    {
811       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
812    }
813    return true;
814 }
815
816 static bool locationscmd(UAContext *ua, const char *cmd)
817 {
818    if (!open_client_db(ua)) {
819       return true;
820    }
821    if (!db_sql_query(ua->db, 
822                   "SELECT DISTINCT Location FROM Location ORDER BY Location",
823                   one_handler, (void *)ua)) 
824    {
825       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
826    }
827    return true;
828 }
829
830 static bool levelscmd(UAContext *ua, const char *cmd)
831 {
832    ua->send_msg("Incremental\n");
833    ua->send_msg("Full\n");
834    ua->send_msg("Differential\n");
835    ua->send_msg("VirtualFull\n");
836    ua->send_msg("Catalog\n");
837    ua->send_msg("InitCatalog\n");
838    ua->send_msg("VolumeToCatalog\n");
839    ua->send_msg("Base\n");
840    return true;
841 }
842
843 static bool volstatuscmd(UAContext *ua, const char *cmd)
844 {
845    ua->send_msg("Append\n");
846    ua->send_msg("Full\n");
847    ua->send_msg("Used\n");
848    ua->send_msg("Recycle\n");
849    ua->send_msg("Purged\n");
850    ua->send_msg("Cleaning\n");
851    ua->send_msg("Error\n");
852    return true;
853 }
854
855 /*
856  * Return default values for a job
857  */
858 static bool defaultscmd(UAContext *ua, const char *cmd)
859 {
860    JOB *job;
861    CLIENT *client;
862    STORE *storage;
863    POOL *pool;
864    char ed1[50];
865
866    if (ua->argc != 2 || !ua->argv[1]) {
867       return true;
868    }
869
870    /* Job defaults */   
871    if (strcmp(ua->argk[1], "job") == 0) {
872       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
873          return true;
874       }
875       job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
876       if (job) {
877          USTORE store;
878          ua->send_msg("job=%s", job->name());
879          ua->send_msg("pool=%s", job->pool->name());
880          ua->send_msg("messages=%s", job->messages->name());
881          ua->send_msg("client=%s", job->client->name());
882          get_job_storage(&store, job, NULL);
883          ua->send_msg("storage=%s", store.store->name());
884          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
885          ua->send_msg("level=%s", level_to_str(job->JobLevel));
886          ua->send_msg("type=%s", job_type_to_str(job->JobType));
887          ua->send_msg("fileset=%s", job->fileset->name());
888          ua->send_msg("enabled=%d", job->enabled);
889          ua->send_msg("catalog=%s", job->client->catalog->name());
890       }
891    } 
892    /* Client defaults */
893    else if (strcmp(ua->argk[1], "client") == 0) {
894       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
895          return true;   
896       }
897       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
898       if (client) {
899          ua->send_msg("client=%s", client->name());
900          ua->send_msg("address=%s", client->address);
901          ua->send_msg("fdport=%d", client->FDport);
902          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
903          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
904          ua->send_msg("autoprune=%d", client->AutoPrune);
905          ua->send_msg("catalog=%s", client->catalog->name());
906       }
907    }
908    /* Storage defaults */
909    else if (strcmp(ua->argk[1], "storage") == 0) {
910       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
911          return true;
912       }
913       storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
914       DEVICE *device;
915       if (storage) {
916          ua->send_msg("storage=%s", storage->name());
917          ua->send_msg("address=%s", storage->address);
918          ua->send_msg("enabled=%d", storage->enabled);
919          ua->send_msg("media_type=%s", storage->media_type);
920          ua->send_msg("sdport=%d", storage->SDport);
921          device = (DEVICE *)storage->device->first();
922          ua->send_msg("device=%s", device->name());
923          if (storage->device->size() > 1) {
924             while ((device = (DEVICE *)storage->device->next())) {
925                ua->send_msg(",%s", device->name());
926             }
927          }
928       }
929    }
930    /* Pool defaults */
931    else if (strcmp(ua->argk[1], "pool") == 0) {
932       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
933          return true;
934       }
935       pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
936       if (pool) {
937          ua->send_msg("pool=%s", pool->name());
938          ua->send_msg("pool_type=%s", pool->pool_type);
939          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
940          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
941          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
942          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
943          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
944          ua->send_msg("max_volumes=%d", pool->max_volumes);
945          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
946          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
947          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
948          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
949          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
950          ua->send_msg("auto_prune=%d", pool->AutoPrune);
951          ua->send_msg("recycle=%d", pool->Recycle);
952          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
953          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
954       }
955    }
956    return true;
957 }