1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2011-2015 Planets Communications B.V.
5    Copyright (C) 2013-2019 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Marco van Wieringen, May 2015
24  */
25 /**
26  * @file
27  * Generic NDMP Data Management Application (DMA) routines
28  */
29 
30 #include "include/bareos.h"
31 #include "dird.h"
32 #include "dird/jcr_private.h"
33 #include "dird/dird_globals.h"
34 #include "include/auth_protocol_types.h"
35 #include "include/auth_types.h"
36 
37 #if HAVE_NDMP
38 #  define SMTAPE_MIN_BLOCKSIZE 4096        /**< 4 Kb */
39 #  define SMTAPE_MAX_BLOCKSIZE 262144      /**< 256 Kb */
40 #  define SMTAPE_BLOCKSIZE_INCREMENTS 4096 /**< 4 Kb */
41 
42 #  include "ndmp/ndmagents.h"
43 #  include "ndmp_dma_priv.h"
44 #endif /* HAVE_NDMP */
45 
46 namespace directordaemon {
47 
48 #if HAVE_NDMP
49 /* Imported variables */
50 
51 /* Forward referenced functions */
52 
53 /**
54  * Per NDMP format specific options.
55  */
56 static ndmp_backup_format_option ndmp_backup_format_options[] = {
57     /* format uses_file_history  uses_level restore_prefix_relative
58        needs_namelist */
59     {(char*)"dump", true, true, true, true},
60     {(char*)"tar", true, false, true, true},
61     {(char*)"smtape", false, true, false, true},
62     {(char*)"zfs", false, true, false, true},
63     {(char*)"vbb", false, true, false, true},
64     {(char*)"image", false, true, false, true},
65     {NULL, false, false, false}};
66 
67 /**
68  * find ndmp_backup_format_option for given format
69  * @param [in] backup_format string specifying the backup format
70  * @return returns a pointer to the ndmp_backup_format_option
71  */
ndmp_lookup_backup_format_options(const char * backup_format)72 ndmp_backup_format_option* ndmp_lookup_backup_format_options(
73     const char* backup_format)
74 {
75   int i = 0;
76 
77   while (ndmp_backup_format_options[i].format) {
78     if (Bstrcasecmp(backup_format, ndmp_backup_format_options[i].format)) {
79       break;
80     }
81     i++;
82   }
83 
84   if (ndmp_backup_format_options[i].format) {
85     return &ndmp_backup_format_options[i];
86   }
87 
88   return (ndmp_backup_format_option*)NULL;
89 }
90 
91 /**
92  * Validation functions.
93  */
NdmpValidateClient(JobControlRecord * jcr)94 bool NdmpValidateClient(JobControlRecord* jcr)
95 {
96   switch (jcr->impl->res.client->Protocol) {
97     case APT_NDMPV2:
98     case APT_NDMPV3:
99     case APT_NDMPV4:
100       if (jcr->impl->res.client->password_.encoding != p_encoding_clear) {
101         Jmsg(jcr, M_FATAL, 0,
102              _("Client %s, has incompatible password encoding for running NDMP "
103                "backup.\n"),
104              jcr->impl->res.client->resource_name_);
105         return false;
106       }
107       break;
108     default:
109       Jmsg(jcr, M_FATAL, 0,
110            _("Client %s, with backup protocol %s  not compatible for running "
111              "NDMP backup.\n"),
112            jcr->impl->res.client->resource_name_,
113            AuthenticationProtocolTypeToString(jcr->impl->res.client->Protocol));
114       return false;
115   }
116 
117   return true;
118 }
119 
NdmpValidateStorage(JobControlRecord * jcr,StorageResource * store)120 static inline bool NdmpValidateStorage(JobControlRecord* jcr,
121                                        StorageResource* store)
122 {
123   switch (store->Protocol) {
124     case APT_NDMPV2:
125     case APT_NDMPV3:
126     case APT_NDMPV4:
127       if (store->password_.encoding != p_encoding_clear) {
128         Jmsg(jcr, M_FATAL, 0,
129              _("Storage %s, has incompatible password encoding for running "
130                "NDMP backup.\n"),
131              store->resource_name_);
132         return false;
133       }
134       break;
135     default:
136       Jmsg(jcr, M_FATAL, 0,
137            _("Storage %s has illegal backup protocol %s for NDMP backup\n"),
138            store->resource_name_,
139            AuthenticationProtocolTypeToString(store->Protocol));
140       return false;
141   }
142 
143   return true;
144 }
145 
NdmpValidateStorage(JobControlRecord * jcr)146 bool NdmpValidateStorage(JobControlRecord* jcr)
147 {
148   StorageResource* store = nullptr;
149 
150   if (jcr->impl->res.write_storage_list) {
151     foreach_alist (store, jcr->impl->res.write_storage_list) {
152       if (!NdmpValidateStorage(jcr, store)) { return false; }
153     }
154   } else {
155     foreach_alist (store, jcr->impl->res.read_storage_list) {
156       if (!NdmpValidateStorage(jcr, store)) { return false; }
157     }
158   }
159 
160   return true;
161 }
162 
NdmpValidateJob(JobControlRecord * jcr,struct ndm_job_param * job)163 bool NdmpValidateJob(JobControlRecord* jcr, struct ndm_job_param* job)
164 {
165   int n_err, i;
166   char audit_buffer[256];
167 
168   /*
169    * Audit the job so we only submit a valid NDMP job.
170    */
171   n_err = 0;
172   i = 0;
173   do {
174     n_err = ndma_job_audit(job, audit_buffer, i);
175     if (n_err) {
176       Jmsg(jcr, M_ERROR, 0, _("NDMP Job validation error = %s\n"),
177            audit_buffer);
178     }
179     i++;
180   } while (i < n_err);
181 
182   if (n_err) { return false; }
183 
184   return true;
185 }
186 
187 /**
188  * Fill a ndmagent structure with the correct info. Instead of calling
189  * ndmagent_from_str we fill the structure ourself from info provides in a
190  * resource.
191  */
fill_ndmp_agent_config(JobControlRecord * jcr,struct ndmagent * agent,uint32_t protocol,uint32_t authtype,char * address,uint32_t port,char * username,char * password)192 static inline bool fill_ndmp_agent_config(JobControlRecord* jcr,
193                                           struct ndmagent* agent,
194                                           uint32_t protocol,
195                                           uint32_t authtype,
196                                           char* address,
197                                           uint32_t port,
198                                           char* username,
199                                           char* password)
200 {
201   agent->conn_type = NDMCONN_TYPE_REMOTE;
202   switch (protocol) {
203     case APT_NDMPV2:
204       agent->protocol_version = 2;
205       break;
206     case APT_NDMPV3:
207       agent->protocol_version = 3;
208       break;
209     case APT_NDMPV4:
210       agent->protocol_version = 4;
211       break;
212     default:
213       Jmsg(jcr, M_FATAL, 0, _("Illegal protocol %d for NDMP Job\n"), protocol);
214       return false;
215   }
216 
217   switch (authtype) {
218     case AT_NONE:
219       agent->auth_type = 'n';
220       break;
221     case AT_CLEAR:
222       agent->auth_type = 't';
223       break;
224     case AT_MD5:
225       agent->auth_type = 'm';
226       break;
227     case AT_VOID:
228       agent->auth_type = 'v';
229       break;
230     default:
231       Jmsg(jcr, M_FATAL, 0, _("Illegal authtype %d for NDMP Job\n"), authtype);
232       return false;
233   }
234   /*
235    *  sanity checks
236    */
237   if (!address) {
238     Jmsg(jcr, M_FATAL, 0, _("fill_ndmp_agent_config: address is missing\n"));
239     return false;
240   }
241 
242   if (!username) {
243     Jmsg(jcr, M_FATAL, 0, _("fill_ndmp_agent_config: username is missing\n"));
244     return false;
245   }
246 
247   if (!password) {
248     Jmsg(jcr, M_FATAL, 0, _("fill_ndmp_agent_config: password is missing\n"));
249     return false;
250   }
251 
252   if (!port) {
253     Jmsg(jcr, M_FATAL, 0, _("fill_ndmp_agent_config: port is missing\n"));
254     return false;
255   }
256 
257   agent->port = port;
258   bstrncpy(agent->host, address, sizeof(agent->host));
259   bstrncpy(agent->account, username, sizeof(agent->account));
260   bstrncpy(agent->password, password, sizeof(agent->password));
261 
262   return true;
263 }
264 
265 /**
266  * Parse a meta-tag and convert it into a ndmp_pval
267  */
NdmpParseMetaTag(struct ndm_env_table * env_tab,char * meta_tag)268 void NdmpParseMetaTag(struct ndm_env_table* env_tab, char* meta_tag)
269 {
270   char* p;
271   ndmp9_pval pv;
272 
273   /*
274    * See if the meta-tag is parseable.
275    */
276   if ((p = strchr(meta_tag, '=')) == NULL) { return; }
277 
278   /*
279    * Split the tag on the '='
280    */
281   *p = '\0';
282   pv.name = meta_tag;
283   pv.value = p + 1;
284   ndma_update_env_list(env_tab, &pv);
285 
286   /*
287    * Restore the '='
288    */
289   *p = '=';
290 }
291 
292 /**
293  * Calculate the wanted NDMP loglevel from the current debug level and
294  * any configure minimum level.
295  */
NativeToNdmpLoglevel(int NdmpLoglevel,int debuglevel,NIS * nis)296 int NativeToNdmpLoglevel(int NdmpLoglevel, int debuglevel, NIS* nis)
297 {
298   unsigned int level;
299 
300   nis->LogLevel = NdmpLoglevel;
301 
302   /*
303    * NDMP loglevels run from 0 - 9 so we take a look at the
304    * current debug level and divide it by 100 to get a proper
305    * value. If the debuglevel is below the wanted initial level
306    * we set the loglevel to the wanted initial level. As the
307    * debug logging takes care of logging messages that are
308    * unwanted we can set the loglevel higher and still don't
309    * get debug messages.
310    */
311   level = debuglevel / 100;
312   if (level < nis->LogLevel) { level = nis->LogLevel; }
313 
314   /*
315    * Make sure the level is in the wanted range.
316    */
317   if (level > 9) { level = 9; }
318 
319   return level;
320 }
321 
NdmpBuildClientJob(JobControlRecord * jcr,ClientResource * client,StorageResource * store,int operation,struct ndm_job_param * job)322 bool NdmpBuildClientJob(JobControlRecord* jcr,
323                         ClientResource* client,
324                         StorageResource* store,
325                         int operation,
326                         struct ndm_job_param* job)
327 {
328   memset(job, 0, sizeof(struct ndm_job_param));
329 
330   job->operation = operation;
331   job->bu_type = jcr->impl->backup_format;
332 
333   /*
334    * For NDMP the backupformat is a prerequite abort the backup job when
335    * it is not supplied in the config definition.
336    */
337   if (!job->bu_type) {
338     Jmsg(jcr, M_FATAL, 0, _("No backup type specified in NDMP job\n"));
339     goto bail_out;
340   }
341 
342   /*
343    * The data_agent is the client being backed up or restored using NDMP.
344    */
345   ASSERT(client->password_.encoding == p_encoding_clear);
346   if (!fill_ndmp_agent_config(jcr, &job->data_agent, client->Protocol,
347                               client->AuthType, client->address, client->FDport,
348                               client->username, client->password_.value)) {
349     goto bail_out;
350   }
351 
352   /*
353    * The tape_agent is the storage daemon via the NDMP protocol.
354    */
355   ASSERT(store->password_.encoding == p_encoding_clear);
356   if (!fill_ndmp_agent_config(jcr, &job->tape_agent, store->Protocol,
357                               store->AuthType, store->address, store->SDport,
358                               store->username, store->password_.value)) {
359     goto bail_out;
360   }
361 
362   if (Bstrcasecmp(jcr->impl->backup_format, "smtape")) {
363     /*
364      * SMTAPE only wants certain blocksizes.
365      */
366     if (jcr->impl->res.client->ndmp_blocksize < SMTAPE_MIN_BLOCKSIZE
367         || jcr->impl->res.client->ndmp_blocksize > SMTAPE_MAX_BLOCKSIZE) {
368       Jmsg(jcr, M_FATAL, 0,
369            _("For SMTAPE NDMP jobs the NDMP blocksize needs to be between %d "
370              "and %d, but is set to %d\n"),
371            SMTAPE_MIN_BLOCKSIZE, SMTAPE_MAX_BLOCKSIZE,
372            jcr->impl->res.client->ndmp_blocksize);
373       goto bail_out;
374     }
375 
376     if ((jcr->impl->res.client->ndmp_blocksize % SMTAPE_BLOCKSIZE_INCREMENTS)
377         != 0) {
378       Jmsg(jcr, M_FATAL, 0,
379            _("For SMTAPE NDMP jobs the NDMP blocksize needs to be in "
380              "increments of %d bytes, but is set to %d\n"),
381            SMTAPE_BLOCKSIZE_INCREMENTS, jcr->impl->res.client->ndmp_blocksize);
382       goto bail_out;
383     }
384 
385     job->record_size = jcr->impl->res.client->ndmp_blocksize;
386   } else {
387     job->record_size = jcr->impl->res.client->ndmp_blocksize;
388   }
389 
390   return true;
391 
392 bail_out:
393   return false;
394 }
395 
396 
NdmpBuildStorageJob(JobControlRecord * jcr,StorageResource * store,bool init_tape,bool init_robot,int operation,struct ndm_job_param * job)397 bool NdmpBuildStorageJob(JobControlRecord* jcr,
398                          StorageResource* store,
399                          bool init_tape,
400                          bool init_robot,
401                          int operation,
402                          struct ndm_job_param* job)
403 {
404   memset(job, 0, sizeof(struct ndm_job_param));
405 
406   job->operation = operation;
407   job->bu_type = jcr->impl->backup_format;
408 
409   if (!fill_ndmp_agent_config(jcr, &job->data_agent, store->Protocol,
410                               store->AuthType, store->address, store->SDport,
411                               store->username, store->password_.value)) {
412     goto bail_out;
413   }
414 
415 
416   if (init_tape) {
417     /*
418      * Setup the TAPE agent of the NDMP job.
419      */
420     ASSERT(store->password_.encoding == p_encoding_clear);
421     if (!fill_ndmp_agent_config(jcr, &job->tape_agent, store->Protocol,
422                                 store->AuthType, store->address, store->SDport,
423                                 store->username, store->password_.value)) {
424       goto bail_out;
425     }
426   }
427 
428   if (init_robot) {
429     /*
430      * Setup the ROBOT agent of the NDMP job.
431      */
432     if (!fill_ndmp_agent_config(jcr, &job->robot_agent, store->Protocol,
433                                 store->AuthType, store->address, store->SDport,
434                                 store->username, store->password_.value)) {
435       goto bail_out;
436     }
437   }
438 
439 
440   return true;
441 
442 bail_out:
443   return false;
444 }
445 
NdmpBuildClientAndStorageJob(JobControlRecord * jcr,StorageResource * store,ClientResource * client,bool init_tape,bool init_robot,int operation,struct ndm_job_param * job)446 bool NdmpBuildClientAndStorageJob(JobControlRecord* jcr,
447                                   StorageResource* store,
448                                   ClientResource* client,
449                                   bool init_tape,
450                                   bool init_robot,
451                                   int operation,
452                                   struct ndm_job_param* job)
453 {
454   /*
455    * setup storage job
456    * i.e. setup tape_agent and robot_agent
457    */
458   if (!NdmpBuildStorageJob(jcr, store, init_tape, init_robot, operation, job)) {
459     goto bail_out;
460   }
461 
462   /*
463    * now configure client job
464    * i.e. setup data_agent
465    */
466   if (!fill_ndmp_agent_config(jcr, &job->data_agent, client->Protocol,
467                               client->AuthType, client->address, client->FDport,
468                               client->username, client->password_.value)) {
469     goto bail_out;
470   }
471 
472   return true;
473 
474 bail_out:
475   return false;
476 }
477 
478 /**
479  * Interface function which glues the logging infra of the NDMP lib with the
480  * daemon.
481  */
NdmpLoghandler(struct ndmlog * log,char * tag,int level,char * msg)482 void NdmpLoghandler(struct ndmlog* log, char* tag, int level, char* msg)
483 {
484   int internal_level;
485   NIS* nis;
486 
487   /*
488    * We don't want any trailing newline in log messages.
489    */
490   StripTrailingNewline(msg);
491 
492   /*
493    * Make sure if the logging system was setup properly.
494    */
495   nis = (NIS*)log->ctx;
496   if (!nis) { return; }
497 
498   /*
499    * If the log level of this message is under our logging treshold we
500    * log it as part of the Job.
501    */
502   if (level <= (int)nis->LogLevel) {
503     if (nis->jcr) {
504       /*
505        * Look at the tag field to see what is logged.
506        */
507       if (bstrncmp(tag + 1, "LM", 2)) {
508         /*
509          * *LM* messages. E.g. log message NDMP protocol msgs.
510          * First character of the tag is the agent sending the
511          * message e.g. 'D' == Data Agent
512          *              'T' == Tape Agent
513          *              'R' == Robot Agent
514          *              'C' == Control Agent (DMA)
515          *
516          * Last character is the type of message e.g.
517          * 'n' - normal message
518          * 'd' - debug message
519          * 'e' - error message
520          * 'w' - warning message
521          * '?' - unknown message level
522          */
523         switch (*(tag + 3)) {
524           case 'n':
525             Jmsg(nis->jcr, M_INFO, 0, "%s\n", msg);
526             break;
527           case 'e':
528             Jmsg(nis->jcr, M_ERROR, 0, "%s\n", msg);
529             break;
530           case 'w':
531             Jmsg(nis->jcr, M_WARNING, 0, "%s\n", msg);
532             break;
533           case '?':
534             Jmsg(nis->jcr, M_INFO, 0, "%s\n", msg);
535             break;
536           default:
537             break;
538         }
539       } else {
540         Jmsg(nis->jcr, M_INFO, 0, "%s\n", msg);
541       }
542     }
543   }
544 
545   /*
546    * Print any debug message we convert the NDMP level back to an internal
547    * level and let the normal debug logging handle if it needs to be printed
548    * or not.
549    */
550   internal_level = level * 100;
551   Dmsg3(internal_level, "NDMP: [%s] [%d] %s\n", tag, level, msg);
552 }
553 
554 /**
555  * Interface function which glues the logging infra of the NDMP lib to Dmsg
556  * output
557  */
ndmp_log_delivery_cb_to_dmsg(struct ndmlog * log,char * tag,int lev,char * msg)558 extern "C" void ndmp_log_delivery_cb_to_dmsg(struct ndmlog* log,
559                                              char* tag,
560                                              int lev,
561                                              char* msg)
562 {
563   NIS* nis;
564 
565   /*
566    * Make sure if the logging system was setup properly.
567    */
568   nis = (NIS*)log->ctx;
569   if (!nis || !nis->jcr) { return; }
570 
571   Dmsg1((int)nis->LogLevel, "%s\n", msg);
572 }
573 
574 
575 /**
576  * Interface function which glues the logging infra of the NDMP lib to Jmsg
577  * output
578  */
ndmp_log_delivery_cb_to_jmsg(struct ndmlog * log,char * tag,int lev,char * msg)579 extern "C" void ndmp_log_delivery_cb_to_jmsg(struct ndmlog* log,
580                                              char* tag,
581                                              int lev,
582                                              char* msg)
583 {
584   NIS* nis;
585 
586   /*
587    * Make sure if the logging system was setup properly.
588    */
589   nis = (NIS*)log->ctx;
590   if (!nis || !nis->jcr) { return; }
591 
592   Jmsg(nis->jcr, M_INFO, 0, "%s\n", msg);
593 }
594 
595 /**
596  * Interface function which glues the logging infra of the NDMP lib with the
597  * user agent
598  */
ndmp_log_delivery_cb_to_ua(struct ndmlog * log,char * tag,int lev,char * msg)599 extern "C" void ndmp_log_delivery_cb_to_ua(struct ndmlog* log,
600                                            char* tag,
601                                            int lev,
602                                            char* msg)
603 {
604   NIS* nis;
605 
606   /*
607    * Make sure if the logging system was setup properly.
608    */
609   nis = (NIS*)log->ctx;
610   if (!nis) { return; }
611 
612   nis->ua->SendMsg("%s\n", msg);
613 }
614 
615 /**
616  * Generic function to query the NDMP server using the NDM_JOB_OP_QUERY_AGENTS
617  * operation. Callback is the above NdmpClientStatusHandler which prints
618  * the data to the user context.
619  */
NdmpDoQuery(UaContext * ua,JobControlRecord * jcr,ndm_job_param * ndmp_job,int NdmpLoglevel,ndmca_query_callbacks * query_cbs)620 void NdmpDoQuery(UaContext* ua,
621                  JobControlRecord* jcr,
622                  ndm_job_param* ndmp_job,
623                  int NdmpLoglevel,
624                  ndmca_query_callbacks* query_cbs)
625 {
626   NIS* nis;
627   struct ndm_session ndmp_sess;
628   JobControlRecord* local_jcr = nullptr;
629   /*
630    * Initialize a new NDMP session
631    */
632   memset(&ndmp_sess, 0, sizeof(ndmp_sess));
633   ndmp_sess.param
634       = (struct ndm_session_param*)malloc(sizeof(struct ndm_session_param));
635   memset(ndmp_sess.param, 0, sizeof(struct ndm_session_param));
636   ndmp_sess.param->log.deliver = NdmpLoghandler;
637   nis = (NIS*)malloc(sizeof(NIS));
638   memset(nis, 0, sizeof(NIS));
639   ndmp_sess.param->log_level
640       = NativeToNdmpLoglevel(NdmpLoglevel, debug_level, nis);
641   nis->ua = ua;
642   ndmp_sess.param->log.ctx = nis;
643   ndmp_sess.conn_snooping = (me->ndmp_snooping) ? 1 : 0;
644   ndmp_sess.control_agent_enabled = 1;
645 
646   if (ua) {
647     nis->ua = ua;
648     local_jcr = ua->jcr;
649     ndmp_sess.param->log.deliver = ndmp_log_delivery_cb_to_ua;
650 
651   } else if (jcr) {
652     nis->jcr = jcr;
653     local_jcr = jcr;
654     ndmp_sess.param->log.deliver = ndmp_log_delivery_cb_to_dmsg;
655 
656   } else {
657     goto bail_out;
658   }
659   /*
660    * Register the query callbacks that give us the query results
661    */
662   ndmca_query_register_callbacks(&ndmp_sess, query_cbs);
663 
664   /*
665    * Initialize the session structure.
666    */
667   if (ndma_session_initialize(&ndmp_sess)) { goto bail_out; }
668 
669   /*
670    * Copy the actual job to perform.
671    */
672   memcpy(&ndmp_sess.control_acb->job, ndmp_job, sizeof(struct ndm_job_param));
673   if (!NdmpValidateJob(local_jcr, &ndmp_sess.control_acb->job)) {
674     goto cleanup;
675   }
676 
677   /*
678    * Commission the session for a run.
679    */
680   if (ndma_session_commission(&ndmp_sess)) { goto cleanup; }
681 
682   /*
683    * Setup the DMA.
684    */
685   if (ndmca_connect_control_agent(&ndmp_sess)) { goto cleanup; }
686 
687   ndmp_sess.conn_open = 1;
688   ndmp_sess.conn_authorized = 1;
689 
690   /*
691    * Let the DMA perform its magic.
692    */
693   if (ndmca_control_agent(&ndmp_sess) != 0) { goto cleanup; }
694 
695 cleanup:
696   /*
697    * Destroy the session.
698    */
699   ndma_session_destroy(&ndmp_sess);
700 
701 bail_out:
702 
703   ndmca_query_unregister_callbacks(&ndmp_sess);
704   /*
705    * Free the param block.
706    */
707   if (ndmp_sess.param->log_tag) { free(ndmp_sess.param->log_tag); }
708   free(ndmp_sess.param);
709   free(nis);
710   ndmp_sess.param = NULL;
711 }
712 
713 /**
714  * Output the status of a NDMP client. Query the DATA agent of a
715  * native NDMP server to give some info.
716  */
DoNdmpClientStatus(UaContext * ua,ClientResource * client,char * cmd)717 void DoNdmpClientStatus(UaContext* ua, ClientResource* client, char* cmd)
718 {
719   struct ndm_job_param ndmp_job;
720 
721   memset(&ndmp_job, 0, sizeof(struct ndm_job_param));
722   ndmp_job.operation = NDM_JOB_OP_QUERY_AGENTS;
723 
724   /*
725    * Query the DATA agent of the NDMP server.
726    */
727   ASSERT(client->password_.encoding == p_encoding_clear);
728   if (!fill_ndmp_agent_config(ua->jcr, &ndmp_job.data_agent, client->Protocol,
729                               client->AuthType, client->address, client->FDport,
730                               client->username, client->password_.value)) {
731     return;
732   }
733 
734   ndmca_query_callbacks* query_cbs = nullptr;
735   NdmpDoQuery(ua, NULL, &ndmp_job,
736               (client->ndmp_loglevel > me->ndmp_loglevel)
737                   ? client->ndmp_loglevel
738                   : me->ndmp_loglevel,
739               query_cbs);
740 }
741 #else
742 void DoNdmpClientStatus(UaContext* ua, ClientResource* client, char* cmd)
743 {
744   Jmsg(ua->jcr, M_FATAL, 0, _("NDMP protocol not supported\n"));
745 }
746 
747 #endif /* HAVE_NDMP */
748 } /* namespace directordaemon */
749