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