1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2011-2012 Planets Communications B.V.
5    Copyright (C) 2013-2020 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 2012
24  */
25 /**
26  * @file
27  * ndmp_tape.c implements the NDMP TAPE service which interfaces to
28  * the internal Bareos infrastructure. This is implemented as a separate
29  * daemon protocol on a different port (10000 NDMP by default) which
30  * interfaces to the standard Bareos storage daemon at the record level.
31  *
32  * E.g. normal data from a FD comes via the 9103 port and then get turned
33  * into records for NDMP packets travel via the NDMP protocol library
34  * which is named libbareosndmp and the data gets turned into native Bareos
35  * tape records.
36  */
37 
38 #include "include/bareos.h"
39 
40 #if HAVE_NDMP
41 
42 #  include "stored/stored.h"
43 #  include "stored/stored_globals.h"
44 
45 #  include "ndmp/ndmagents.h"
46 #  include "stored/acquire.h"
47 #  include "stored/bsr.h"
48 #  include "stored/device.h"
49 #  include "stored/device_control_record.h"
50 #  include "stored/jcr_private.h"
51 #  include "stored/label.h"
52 #  include "stored/mount.h"
53 #  include "stored/read_record.h"
54 #  include "stored/spool.h"
55 #  include "lib/address_conf.h"
56 #  include "lib/attribs.h"
57 #  include "lib/berrno.h"
58 #  include "lib/edit.h"
59 #  include "lib/bpoll.h"
60 #  include "lib/parse_conf.h"
61 #  include "lib/thread_list.h"
62 #  include "include/auth_types.h"
63 #  include "include/jcr.h"
64 
65 #  include <algorithm>
66 #  include <netinet/in.h>
67 #  include <sys/socket.h>
68 #  include <vector>
69 #  include <arpa/inet.h>
70 #  include <netdb.h>
71 #  ifdef HAVE_ARPA_NAMESER_H
72 #    include <arpa/nameser.h>
73 #  endif
74 
75 #  ifdef HAVE_POLL_H
76 #    include <poll.h>
77 #  elif HAVE_SYS_POLL_H
78 #    include <sys/poll.h>
79 #  endif
80 #endif /* #if HAVE_NDMP */
81 
82 namespace storagedaemon {
83 #if HAVE_NDMP
84 
85 /**
86  * Structure used to pass arguments to the ndmp_thread_server thread
87  * via a void * argument. Things like the addresslist, maximum number
88  * of clients and the client thread list to use are passed using this
89  * structure.
90  */
91 struct ndmp_thread_server_args {
92   dlist* addr_list;
93   int max_clients;
94   ThreadList* thread_list;
95 };
96 
97 /**
98  * Internal structure to keep track of private data for a NDMP session.
99  * Referenced via (struct ndm_session)->session_handle.
100  */
101 struct ndmp_session_handle {
102   int fd;                       /* Socket file descriptor */
103   char* host;                   /* Host name/IP */
104   int port;                     /* Local port */
105   struct sockaddr client_addr;  /* Client's IP address */
106   struct sockaddr_in peer_addr; /* Peer's IP address */
107   JobControlRecord*
108       jcr; /* Internal JobControlRecord bound to this NDMP session */
109 };
110 
111 /**
112  * Internal structure to keep track of private data.
113  */
114 struct ndmp_internal_state {
115   uint32_t LogLevel;
116   JobControlRecord* jcr;
117 };
118 typedef struct ndmp_internal_state NIS;
119 
120 #  if HAVE_NDMP
121 static ThreadList thread_list;
122 #  endif
123 
124 /* Static globals */
125 static bool quit = false;
126 static bool ndmp_initialized = false;
127 static pthread_t ndmp_tid;
128 static struct ndmp_thread_server_args ndmp_thread_server_args;
129 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
130 
131 /* Forward referenced functions */
132 
NativeToNdmpLoglevel(int debuglevel,NIS * nis)133 static inline int NativeToNdmpLoglevel(int debuglevel, NIS* nis)
134 {
135   unsigned int level;
136 
137   memset(nis, 0, sizeof(NIS));
138 
139   /*
140    * Lookup the initial default log_level from the default StorageResource.
141    */
142   nis->LogLevel = me->ndmploglevel;
143 
144   /*
145    * NDMP loglevels run from 0 - 9 so we take a look at the
146    * current debug level and divide it by 100 to get a proper
147    * value. If the debuglevel is below the wanted initial level
148    * we set the loglevel to the wanted initial level. As the
149    * debug logging takes care of logging messages that are
150    * unwanted we can set the loglevel higher and still don't
151    * get debug messages.
152    */
153   level = debuglevel / 100;
154   if (level < nis->LogLevel) { level = nis->LogLevel; }
155 
156   /*
157    * Make sure the level is in the wanted range.
158    */
159   if (level > 9) { level = 9; }
160 
161   return level;
162 }
163 
164 /**
165  * Interface function which glues the logging infra of the NDMP lib with the
166  * daemon.
167  */
NdmpLoghandler(struct ndmlog * log,char * tag,int level,char * msg)168 void NdmpLoghandler(struct ndmlog* log, char* tag, int level, char* msg)
169 {
170   int internal_level;
171   NIS* nis;
172 
173   /*
174    * We don't want any trailing newline in log messages.
175    */
176   StripTrailingNewline(msg);
177 
178   /*
179    * Make sure if the logging system was setup properly.
180    */
181   nis = (NIS*)log->ctx;
182   if (!nis) { return; }
183 
184   /*
185    * If the log level of this message is under our logging treshold we
186    * log it as part of the Job.
187    */
188   if (level <= (int)nis->LogLevel) {
189     if (nis->jcr) {
190       /*
191        * Look at the tag field to see what is logged.
192        */
193       if (bstrncmp(tag + 1, "LM", 2)) {
194         /*
195          * *LM* messages. E.g. log message NDMP protocol msgs.
196          * First character of the tag is the agent sending the
197          * message e.g. 'D' == Data Agent
198          *              'T' == Tape Agent
199          *              'R' == Robot Agent
200          *              'C' == Control Agent (DMA)
201          *
202          * Last character is the type of message e.g.
203          * 'n' - normal message
204          * 'd' - debug message
205          * 'e' - error message
206          * 'w' - warning message
207          * '?' - unknown message level
208          */
209         switch (*(tag + 3)) {
210           case 'n':
211             Jmsg(nis->jcr, M_INFO, 0, "%s\n", msg);
212             break;
213           case 'e':
214             Jmsg(nis->jcr, M_ERROR, 0, "%s\n", msg);
215             break;
216           case 'w':
217             Jmsg(nis->jcr, M_WARNING, 0, "%s\n", msg);
218             break;
219           case '?':
220             Jmsg(nis->jcr, M_INFO, 0, "%s\n", msg);
221             break;
222           default:
223             break;
224         }
225       } else {
226         Jmsg(nis->jcr, M_INFO, 0, "%s\n", msg);
227       }
228     }
229   }
230 
231   /*
232    * Print any debug message we convert the NDMP level back to an internal
233    * level and let the normal debug logging handle if it needs to be printed
234    * or not.
235    */
236   internal_level = level * 100;
237   Dmsg3(internal_level, "NDMP: [%s] [%d] %s\n", tag, level, msg);
238 }
239 
240 /**
241  * Clear text authentication callback.
242  */
BndmpAuthClear(struct ndm_session * sess,char * name,char * pass)243 extern "C" int BndmpAuthClear(struct ndm_session* sess, char* name, char* pass)
244 {
245   NdmpResource* auth_config;
246 
247   foreach_res (auth_config, R_NDMP) {
248     /*
249      * Only consider entries for AT_CLEAR authentication type.
250      */
251     if (auth_config->AuthType != AT_CLEAR) { continue; }
252 
253     ASSERT(auth_config->password.encoding == p_encoding_clear);
254 
255     if (bstrcmp(name, auth_config->username)
256         && bstrcmp(pass, auth_config->password.value)) {
257       /*
258        * See if we need to adjust the logging level.
259        */
260       if (sess->param->log.ctx) {
261         NIS* nis;
262 
263         nis = (NIS*)sess->param->log.ctx;
264         if (nis->LogLevel != auth_config->LogLevel) {
265           if (auth_config->LogLevel <= 9) {
266             nis->LogLevel = auth_config->LogLevel;
267           }
268         }
269       }
270 
271       return 1;
272     }
273   }
274   return 0;
275 }
276 
277 /**
278  * MD5 authentication callback.
279  */
bndmp_auth_md5(struct ndm_session * sess,char * name,char digest[16])280 extern "C" int bndmp_auth_md5(struct ndm_session* sess,
281                               char* name,
282                               char digest[16])
283 {
284   NdmpResource* auth_config;
285 
286   foreach_res (auth_config, R_NDMP) {
287     /*
288      * Only consider entries for AT_MD5 authentication type.
289      */
290     if (auth_config->AuthType != AT_MD5) { continue; }
291 
292     if (!bstrcmp(name, auth_config->username)) { continue; }
293 
294     ASSERT(auth_config->password.encoding == p_encoding_clear);
295 
296     if (!ndmmd5_ok_digest(sess->md5_challenge, auth_config->password.value,
297                           digest)) {
298       return 0;
299     }
300 
301     /*
302      * See if we need to adjust the logging level.
303      */
304     if (sess->param->log.ctx) {
305       NIS* nis;
306 
307       nis = (NIS*)sess->param->log.ctx;
308       if (nis->LogLevel != auth_config->LogLevel) {
309         if (auth_config->LogLevel <= 9) {
310           nis->LogLevel = auth_config->LogLevel;
311         }
312       }
313     }
314 
315     return 1;
316   }
317 
318   return 0;
319 }
320 
321 /**
322  * Save a record using the native routines.
323  */
bndmp_write_data_to_block(JobControlRecord * jcr,int stream,char * data,uint32_t data_length)324 static inline bool bndmp_write_data_to_block(JobControlRecord* jcr,
325                                              int stream,
326                                              char* data,
327                                              uint32_t data_length)
328 {
329   bool retval = false;
330   DeviceControlRecord* dcr = jcr->impl->dcr;
331   POOLMEM* rec_data;
332 
333   if (!dcr) {
334     Dmsg0(100, "No dcr defined, bailing out\n");
335     return retval;
336   }
337 
338   if (!dcr->rec) {
339     Dmsg0(100, "No dcr->rec defined, bailing out\n");
340     return retval;
341   }
342   /*
343    * Keep track of the original data buffer and restore it on exit from this
344    * function.
345    */
346   rec_data = dcr->rec->data;
347 
348   dcr->rec->VolSessionId = jcr->VolSessionId;
349   dcr->rec->VolSessionTime = jcr->VolSessionTime;
350   dcr->rec->FileIndex = dcr->FileIndex;
351   dcr->rec->Stream = stream;
352   dcr->rec->maskedStream = stream & STREAMMASK_TYPE; /* strip high bits */
353   dcr->rec->data = data;
354   dcr->rec->data_len = data_length;
355 
356   if (!dcr->WriteRecord()) { goto bail_out; }
357 
358   if (stream == STREAM_UNIX_ATTRIBUTES) {
359     dcr->DirUpdateFileAttributes(dcr->rec);
360   }
361 
362   retval = true;
363 
364 bail_out:
365   dcr->rec->data = rec_data;
366   return retval;
367 }
368 
369 /**
370  * Read a record using the native routines.
371  *
372  * data_length == 0 = EOF
373  */
bndmp_read_data_from_block(JobControlRecord * jcr,char * data,uint32_t wanted_data_length,uint32_t * data_length)374 static inline bool bndmp_read_data_from_block(JobControlRecord* jcr,
375                                               char* data,
376                                               uint32_t wanted_data_length,
377                                               uint32_t* data_length)
378 {
379   DeviceControlRecord* dcr = jcr->impl->read_dcr;
380   READ_CTX* rctx = jcr->impl->read_session.rctx;
381   bool done = false;
382   bool ok = true;
383 
384   if (!rctx) { return false; }
385 
386   while (!done) {
387     /*
388      * See if there are any records left to process.
389      */
390     if (!IsBlockEmpty(rctx->rec)) {
391       if (!ReadNextRecordFromBlock(dcr, rctx, &done)) {
392         /*
393          * When the done flag is set to true we are done reading all
394          * records or end of block read next block.
395          */
396         continue;
397       }
398     } else {
399       /*
400        * Read the next block into our buffers.
401        */
402       if (!ReadNextBlockFromDevice(dcr, &rctx->sessrec, NULL,
403                                    MountNextReadVolume, &ok)) {
404         return false;
405       }
406 
407       /*
408        * Get a new record for each Job as defined by VolSessionId and
409        * VolSessionTime
410        */
411       if (!rctx->rec || rctx->rec->VolSessionId != dcr->block->VolSessionId
412           || rctx->rec->VolSessionTime != dcr->block->VolSessionTime) {
413         ReadContextSetRecord(dcr, rctx);
414       }
415 
416       rctx->records_processed = 0;
417       ClearAllBits(REC_STATE_MAX, rctx->rec->state_bits);
418       rctx->lastFileIndex = READ_NO_FILEINDEX;
419 
420       if (!ReadNextRecordFromBlock(dcr, rctx, &done)) {
421         /*
422          * When the done flag is set to true we are done reading all
423          * records or end of block read next block.
424          */
425         continue;
426       }
427     }
428 
429     /*
430      * See if we are processing some sort of label?
431      */
432     if (rctx->rec->FileIndex < 0) { continue; }
433 
434     /*
435      * Here we should have read a record from the block which contains some
436      * data. Its either:
437      *
438      * - STREAM_UNIX_ATTRIBUTES
439      *      Which is the start of the dump when we encounter that we just read
440      * the next record.
441      * - STREAM_FILE_DATA
442      *      Normal NDMP data.
443      * - STREAM_NDMP_SEPARATOR
444      *      End of NDMP data stream.
445      *
446      * anything other means a corrupted stream of records and means we give an
447      * EOF.
448      */
449     switch (rctx->rec->maskedStream) {
450       case STREAM_UNIX_ATTRIBUTES:
451         continue;
452       case STREAM_FILE_DATA:
453         if (wanted_data_length < rctx->rec->data_len) {
454           Jmsg0(jcr, M_FATAL, 0,
455                 _("Data read from volume bigger then NDMP databuffer, please "
456                   "increase the NDMP blocksize.\n"));
457           return false;
458         }
459         memcpy(data, rctx->rec->data, rctx->rec->data_len);
460         *data_length = rctx->rec->data_len;
461         return true;
462       case STREAM_NDMP_SEPARATOR:
463         *data_length = 0;
464         return true;
465       default:
466         Jmsg1(jcr, M_ERROR, 0, _("Encountered an unknown stream type %d\n"),
467               rctx->rec->maskedStream);
468         *data_length = 0;
469         return true;
470     }
471   }
472 
473   if (done) { *data_length = 0; }
474 
475   return true;
476 }
477 
478 /**
479  * Generate virtual file attributes for the whole NDMP stream.
480  */
BndmpCreateVirtualFile(JobControlRecord * jcr,char * filename)481 static inline bool BndmpCreateVirtualFile(JobControlRecord* jcr, char* filename)
482 {
483   DeviceControlRecord* dcr = jcr->impl->dcr;
484   struct stat statp;
485   time_t now = time(NULL);
486   PoolMem attribs(PM_NAME), data(PM_NAME);
487   int32_t size;
488 
489   memset(&statp, 0, sizeof(statp));
490   statp.st_mode = 0700 | S_IFREG;
491   statp.st_ctime = now;
492   statp.st_mtime = now;
493   statp.st_atime = now;
494   statp.st_size = -1;
495   statp.st_blksize = 4096;
496   statp.st_blocks = 1;
497 
498   /*
499    * Encode a stat structure into an ASCII string.
500    */
501   EncodeStat(attribs.c_str(), &statp, sizeof(statp), dcr->FileIndex,
502              STREAM_UNIX_ATTRIBUTES);
503 
504   /*
505    * Generate a file attributes stream.
506    *   File_index
507    *   File type
508    *   Filename (full path)
509    *   Encoded attributes
510    *   Link name (if type==FT_LNK or FT_LNKSAVED)
511    *   Encoded extended-attributes (for Win32)
512    *   Delta Sequence Number
513    */
514   size = Mmsg(data, "%ld %d %s%c%s%c%s%c%s%c%d%c",
515               dcr->FileIndex,     /* File_index */
516               FT_REG,             /* File type */
517               filename,           /* Filename (full path) */
518               0, attribs.c_str(), /* Encoded attributes */
519               0, "", /* Link name (if type==FT_LNK or FT_LNKSAVED) */
520               0, "", /* Encoded extended-attributes (for Win32) */
521               0, 0,  /* Delta Sequence Number */
522               0);
523 
524   return bndmp_write_data_to_block(jcr, STREAM_UNIX_ATTRIBUTES, data.c_str(),
525                                    size);
526 }
527 
BndmpSimuFlushWeof(struct ndm_session * sess)528 static int BndmpSimuFlushWeof(struct ndm_session* sess)
529 {
530   struct ndm_tape_agent* ta = sess->tape_acb;
531 
532   if (ta->weof_on_close) {
533     /* best effort */
534     ndmos_tape_wfm(sess);
535   }
536   return 0;
537 }
538 
539 /**
540  * Search the JCRs for one with the given security key.
541  */
get_jcr_by_security_key(char * security_key)542 static inline JobControlRecord* get_jcr_by_security_key(char* security_key)
543 {
544   JobControlRecord* jcr;
545 
546   foreach_jcr (jcr) {
547     if (bstrcmp(jcr->sd_auth_key, security_key)) {
548       jcr->IncUseCount();
549       break;
550     }
551   }
552   endeach_jcr(jcr);
553   return jcr;
554 }
555 
bndmp_tape_open(struct ndm_session * sess,char * drive_name,int will_write)556 extern "C" ndmp9_error bndmp_tape_open(struct ndm_session* sess,
557                                        char* drive_name,
558                                        int will_write)
559 {
560   JobControlRecord* jcr;
561   DeviceControlRecord* dcr;
562   char* filesystem;
563   struct ndmp_session_handle* handle;
564   struct ndm_tape_agent* ta;
565 
566   /*
567    * The drive_name should be in the form <AuthKey>@<file_system>%<dumplevel>
568    */
569   if ((filesystem = strchr(drive_name, '@')) == NULL) {
570     return NDMP9_NO_DEVICE_ERR;
571   }
572 
573   /*
574    * Lookup the jobid the drive_name should contain a valid authentication key.
575    */
576   *filesystem++ = '\0';
577   if (!(jcr = get_jcr_by_security_key(drive_name))) {
578     Jmsg1(NULL, M_FATAL, 0,
579           _("NDMP tape open failed: Security Key not found: %s\n"), drive_name);
580     return NDMP9_NO_DEVICE_ERR;
581   }
582 
583   /*
584    * When we found a JobControlRecord with the wanted security key it also
585    * implictly means the authentication succeeded as the connecting NDMP session
586    * only knows the exact security key as it was inserted by the director.
587    */
588   jcr->authenticated = true;
589 
590   /*
591    * There is a native storage daemon session waiting for the FD to connect.
592    * In NDMP terms this is the same as a FD connecting so wake any waiting
593    * threads.
594    */
595   pthread_cond_signal(&jcr->impl->job_start_wait);
596 
597   /*
598    * Save the JobControlRecord to ndm_session binding so everything furher
599    * knows which JobControlRecord belongs to which NDMP session. We have
600    * a special ndmp_session_handle which we can use to track
601    * session specific information.
602    */
603   handle = (struct ndmp_session_handle*)sess->session_handle;
604 
605   /*
606    * If we already have a JobControlRecord binding for this connection we
607    * release it here as we are about to establish a new binding (e.g. second
608    * NDMP save for the same job) and we should end up with the same binding.
609    */
610   if (handle->jcr) { FreeJcr(handle->jcr); }
611   handle->jcr = jcr;
612 
613   /*
614    * Keep track of the JobControlRecord for logging purposes.
615    */
616   if (sess->param->log.ctx) {
617     NIS* nis;
618 
619     nis = (NIS*)sess->param->log.ctx;
620     nis->jcr = jcr;
621   }
622 
623   /*
624    * Depending on the open mode select the right DeviceControlRecord.
625    */
626   if (will_write) {
627     dcr = jcr->impl->dcr;
628   } else {
629     dcr = jcr->impl->read_dcr;
630   }
631 
632   if (!dcr) {
633     Jmsg0(jcr, M_FATAL, 0, _("DeviceControlRecord is NULL!!!\n"));
634     return NDMP9_NO_DEVICE_ERR;
635   }
636 
637   if (!dcr->dev) {
638     Jmsg0(jcr, M_FATAL, 0, _("Device is NULL!!!\n"));
639     return NDMP9_NO_DEVICE_ERR;
640   }
641 
642   /*
643    * See if need to setup for write or read.
644    */
645   if (will_write) {
646     PoolMem virtual_filename(PM_FNAME);
647 
648     /*
649      * Setup internal system for writing data.
650      */
651     Dmsg1(100, "Start append data. res=%d\n", dcr->dev->NumReserved());
652 
653     /*
654      * One NDMP backup Job can be one or more save sessions so we keep
655      * track if we already acquired the storage.
656      */
657     if (!jcr->impl->acquired_storage) {
658       /*
659        * Actually acquire the device which we reserved.
660        */
661       if (!AcquireDeviceForAppend(dcr)) { goto bail_out; }
662 
663       /*
664        * Let any SD plugin know now its time to setup the record translation
665        * infra.
666        */
667       if (GeneratePluginEvent(jcr, bSdEventSetupRecordTranslation, dcr)
668           != bRC_OK) {
669         goto bail_out;
670       }
671 
672       /*
673        * Keep track that we acquired the storage.
674        */
675       jcr->impl->acquired_storage = true;
676 
677       Dmsg1(50, "Begin append device=%s\n", dcr->dev->print_name());
678 
679       /*
680        * Change the Job to running state.
681        */
682       jcr->sendJobStatus(JS_Running);
683 
684       /*
685        * As we only generate very limited attributes info e.g. one
686        * set per NDMP backup stream we only setup data spooling and not
687        * attribute spooling.
688        */
689 
690       if (!BeginDataSpool(dcr)) { goto bail_out; }
691 
692       /*
693        * Write Begin Session Record
694        */
695       if (!WriteSessionLabel(dcr, SOS_LABEL)) {
696         Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
697               dcr->dev->bstrerror());
698         goto bail_out;
699       }
700 
701       dcr->VolFirstIndex = dcr->VolLastIndex = 0;
702       jcr->run_time = time(NULL); /* start counting time for rates */
703 
704       /*
705        * The session is saved as one file stream.
706        */
707       dcr->FileIndex = 1;
708       jcr->JobFiles = 1;
709     } else {
710       /*
711        * The next session is saved as one file stream.
712        */
713       dcr->FileIndex++;
714       jcr->JobFiles++;
715     }
716 
717     /*
718      * Create a virtual file name @NDMP/<filesystem>%<dumplevel> or
719      * @NDMP/<filesystem> and save the attributes to the director.
720      */
721     Mmsg(virtual_filename, "/@NDMP%s", filesystem);
722     if (!BndmpCreateVirtualFile(jcr, virtual_filename.c_str())) {
723       Jmsg0(jcr, M_FATAL, 0, _("Creating virtual file attributes failed.\n"));
724       goto bail_out;
725     }
726   } else {
727     bool ok = true;
728     READ_CTX* rctx;
729 
730     /*
731      * Setup internal system for reading data (if not done before).
732      */
733     if (!jcr->impl->acquired_storage) {
734       Dmsg0(20, "Start read data.\n");
735 
736       if (jcr->impl->NumReadVolumes == 0) {
737         Jmsg(jcr, M_FATAL, 0, _("No Volume names found for restore.\n"));
738         goto bail_out;
739       }
740 
741       Dmsg2(200, "Found %d volumes names to restore. First=%s\n",
742             jcr->impl->NumReadVolumes, jcr->impl->VolList->VolumeName);
743 
744       /*
745        * Ready device for reading
746        */
747       if (!AcquireDeviceForRead(dcr)) { goto bail_out; }
748 
749       /*
750        * Let any SD plugin know now its time to setup the record translation
751        * infra.
752        */
753       if (GeneratePluginEvent(jcr, bSdEventSetupRecordTranslation, dcr)
754           != bRC_OK) {
755         goto bail_out;
756       }
757 
758       /*
759        * Keep track that we acquired the storage.
760        */
761       jcr->impl->acquired_storage = true;
762 
763       /*
764        * Change the Job to running state.
765        */
766       jcr->sendJobStatus(JS_Running);
767 
768       Dmsg1(50, "Begin reading device=%s\n", dcr->dev->print_name());
769 
770       PositionDeviceToFirstFile(jcr, dcr);
771       jcr->impl->read_session.mount_next_volume = false;
772 
773       /*
774        * Allocate a new read context for this Job.
775        */
776       rctx = new_read_context();
777       jcr->impl->read_session.rctx = rctx;
778 
779       /*
780        * Read the first block and setup record processing.
781        */
782       if (!ReadNextBlockFromDevice(dcr, &rctx->sessrec, NULL,
783                                    MountNextReadVolume, &ok)) {
784         Jmsg1(jcr, M_FATAL, 0, _("Read session label failed. ERR=%s\n"),
785               dcr->dev->bstrerror());
786         goto bail_out;
787       }
788 
789       ReadContextSetRecord(dcr, rctx);
790       rctx->records_processed = 0;
791       ClearAllBits(REC_STATE_MAX, rctx->rec->state_bits);
792       rctx->lastFileIndex = READ_NO_FILEINDEX;
793     }
794   }
795 
796   /*
797    * Setup NDMP states.
798    */
799   ta = sess->tape_acb;
800   ta->tape_fd = 0; /* fake filedescriptor */
801   bzero(&ta->tape_state, sizeof ta->tape_state);
802   ta->tape_state.error = NDMP9_NO_ERR;
803   ta->tape_state.state = NDMP9_TAPE_STATE_OPEN;
804   ta->tape_state.open_mode
805       = will_write ? NDMP9_TAPE_RDWR_MODE : NDMP9_TAPE_READ_MODE;
806   ta->tape_state.file_num.valid = NDMP9_VALIDITY_VALID;
807   ta->tape_state.soft_errors.valid = NDMP9_VALIDITY_VALID;
808   ta->tape_state.block_size.valid = NDMP9_VALIDITY_VALID;
809   ta->tape_state.blockno.valid = NDMP9_VALIDITY_VALID;
810   ta->tape_state.total_space.valid = NDMP9_VALIDITY_INVALID;
811   ta->tape_state.space_remain.valid = NDMP9_VALIDITY_INVALID;
812 
813   return NDMP9_NO_ERR;
814 
815 bail_out:
816   jcr->setJobStatus(JS_ErrorTerminated);
817   return NDMP9_NO_DEVICE_ERR;
818 }
819 
BndmpTapeClose(struct ndm_session * sess)820 extern "C" ndmp9_error BndmpTapeClose(struct ndm_session* sess)
821 {
822   JobControlRecord* jcr;
823   ndmp9_error err;
824   struct ndmp_session_handle* handle;
825   struct ndm_tape_agent* ta = sess->tape_acb;
826   char ndmp_separator[] = {"NdMpSePaRaToR"};
827 
828   if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
829 
830   BndmpSimuFlushWeof(sess);
831 
832   /*
833    * Setup the glue between the NDMP layer and the Storage Daemon.
834    */
835   handle = (struct ndmp_session_handle*)sess->session_handle;
836 
837   jcr = handle->jcr;
838   if (!jcr) {
839     Jmsg0(NULL, M_FATAL, 0, _("JobControlRecord is NULL!!!\n"));
840     return NDMP9_DEV_NOT_OPEN_ERR;
841   }
842 
843   err = NDMP9_NO_ERR;
844   if (NDMTA_TAPE_IS_WRITABLE(ta)) {
845     /*
846      * Write a separator record so on restore we can recognize the different
847      * NDMP datastreams from each other.
848      */
849     if (!bndmp_write_data_to_block(jcr, STREAM_NDMP_SEPARATOR, ndmp_separator,
850                                    13)) {
851       err = NDMP9_IO_ERR;
852     }
853   }
854 
855   pthread_cond_signal(&jcr->impl->job_end_wait); /* wake any waiting thread */
856 
857   ndmos_tape_initialize(sess);
858 
859   return err;
860 }
861 
bndmp_tape_mtio(struct ndm_session * sess,ndmp9_tape_mtio_op op,uint32_t count,uint32_t * resid)862 extern "C" ndmp9_error bndmp_tape_mtio(struct ndm_session* sess,
863                                        ndmp9_tape_mtio_op op,
864                                        uint32_t count,
865                                        uint32_t* resid)
866 {
867   struct ndm_tape_agent* ta = sess->tape_acb;
868 
869   *resid = 0;
870 
871   if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
872 
873   /*
874    * audit for valid op and for tape mode
875    */
876   switch (op) {
877     case NDMP9_MTIO_FSF:
878       return NDMP9_NO_ERR;
879 
880     case NDMP9_MTIO_BSF:
881       return NDMP9_NO_ERR;
882 
883     case NDMP9_MTIO_FSR:
884       return NDMP9_NO_ERR;
885 
886     case NDMP9_MTIO_BSR:
887       return NDMP9_NO_ERR;
888 
889     case NDMP9_MTIO_REW:
890       BndmpSimuFlushWeof(sess);
891       *resid = 0;
892       ta->tape_state.file_num.value = 0;
893       ta->tape_state.blockno.value = 0;
894       break;
895 
896     case NDMP9_MTIO_OFF:
897       return NDMP9_NO_ERR;
898 
899     case NDMP9_MTIO_EOF: /* should be "WFM" write-file-mark */
900       return NDMP9_NO_ERR;
901 
902     default:
903       return NDMP9_ILLEGAL_ARGS_ERR;
904   }
905 
906   return NDMP9_NO_ERR;
907 }
908 
bndmp_tape_write(struct ndm_session * sess,char * buf,uint32_t count,uint32_t * done_count)909 extern "C" ndmp9_error bndmp_tape_write(struct ndm_session* sess,
910                                         char* buf,
911                                         uint32_t count,
912                                         uint32_t* done_count)
913 {
914   JobControlRecord* jcr;
915   ndmp9_error err;
916   struct ndmp_session_handle* handle;
917   struct ndm_tape_agent* ta = sess->tape_acb;
918 
919   if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
920 
921   if (!NDMTA_TAPE_IS_WRITABLE(ta)) { return NDMP9_PERMISSION_ERR; }
922 
923   /*
924    * Setup the glue between the NDMP layer and the Storage Daemon.
925    */
926   handle = (struct ndmp_session_handle*)sess->session_handle;
927 
928   jcr = handle->jcr;
929   if (!jcr) {
930     Jmsg0(NULL, M_FATAL, 0, _("JobControlRecord is NULL!!!\n"));
931     return NDMP9_DEV_NOT_OPEN_ERR;
932   }
933 
934   /*
935    * Turn the NDMP data into a internal record and save it.
936    */
937   if (bndmp_write_data_to_block(jcr, STREAM_FILE_DATA, buf, count)) {
938     ta->tape_state.blockno.value++;
939     *done_count = count;
940     err = NDMP9_NO_ERR;
941   } else {
942     jcr->setJobStatus(JS_ErrorTerminated);
943     err = NDMP9_IO_ERR;
944   }
945 
946   ta->weof_on_close = 1;
947 
948   return err;
949 }
950 
BndmpTapeWfm(struct ndm_session * sess)951 extern "C" ndmp9_error BndmpTapeWfm(struct ndm_session* sess)
952 {
953   ndmp9_error err;
954   struct ndm_tape_agent* ta = sess->tape_acb;
955 
956   ta->weof_on_close = 0;
957 
958   if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
959 
960   if (!NDMTA_TAPE_IS_WRITABLE(ta)) { return NDMP9_PERMISSION_ERR; }
961 
962   err = NDMP9_NO_ERR;
963 
964   return err;
965 }
966 
bndmp_tape_read(struct ndm_session * sess,char * buf,uint32_t count,uint32_t * done_count)967 extern "C" ndmp9_error bndmp_tape_read(struct ndm_session* sess,
968                                        char* buf,
969                                        uint32_t count,
970                                        uint32_t* done_count)
971 {
972   JobControlRecord* jcr;
973   ndmp9_error err;
974   struct ndmp_session_handle* handle;
975   struct ndm_tape_agent* ta = sess->tape_acb;
976 
977   if (ta->tape_fd < 0) { return NDMP9_DEV_NOT_OPEN_ERR; }
978 
979   /*
980    * Setup the glue between the NDMP layer and the Storage Daemon.
981    */
982   handle = (struct ndmp_session_handle*)sess->session_handle;
983 
984   jcr = handle->jcr;
985   if (!jcr) {
986     Jmsg0(NULL, M_FATAL, 0, _("JobControlRecord is NULL!!!\n"));
987     return NDMP9_DEV_NOT_OPEN_ERR;
988   }
989 
990   if (bndmp_read_data_from_block(jcr, buf, count, done_count)) {
991     ta->tape_state.blockno.value++;
992     if (*done_count == 0) {
993       err = NDMP9_EOF_ERR;
994     } else {
995       err = NDMP9_NO_ERR;
996     }
997   } else {
998     jcr->setJobStatus(JS_ErrorTerminated);
999     err = NDMP9_IO_ERR;
1000   }
1001 
1002   return err;
1003 }
1004 
RegisterCallbackHooks(struct ndm_session * sess)1005 static inline void RegisterCallbackHooks(struct ndm_session* sess)
1006 {
1007   struct ndm_auth_callbacks auth_callbacks;
1008   struct ndm_tape_simulator_callbacks tape_callbacks;
1009 
1010   /*
1011    * Register the authentication callbacks.
1012    */
1013   auth_callbacks.validate_password = BndmpAuthClear;
1014   auth_callbacks.validate_md5 = bndmp_auth_md5;
1015   ndmos_auth_register_callbacks(sess, &auth_callbacks);
1016 
1017   /*
1018    * Register the tape simulator callbacks.
1019    */
1020   tape_callbacks.tape_open = bndmp_tape_open;
1021   tape_callbacks.tape_close = BndmpTapeClose;
1022   tape_callbacks.tape_mtio = bndmp_tape_mtio;
1023   tape_callbacks.tape_write = bndmp_tape_write;
1024   tape_callbacks.tape_wfm = BndmpTapeWfm;
1025   tape_callbacks.tape_read = bndmp_tape_read;
1026   ndmos_tape_register_callbacks(sess, &tape_callbacks);
1027 }
1028 
UnregisterCallbackHooks(struct ndm_session * sess)1029 static inline void UnregisterCallbackHooks(struct ndm_session* sess)
1030 {
1031   ndmos_tape_unregister_callbacks(sess);
1032   ndmos_auth_unregister_callbacks(sess);
1033 }
1034 
EndOfNdmpBackup(JobControlRecord * jcr)1035 void EndOfNdmpBackup(JobControlRecord* jcr)
1036 {
1037   DeviceControlRecord* dcr = jcr->impl->dcr;
1038   char ec[50];
1039 
1040   /*
1041    * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
1042    * and the subsequent Jmsg() editing will break
1043    */
1044   int32_t job_elapsed = time(NULL) - jcr->run_time;
1045 
1046   if (job_elapsed <= 0) { job_elapsed = 1; }
1047 
1048   Jmsg(jcr, M_INFO, 0,
1049        _("Elapsed time=%02d:%02d:%02d, Transfer rate=%s Bytes/second\n"),
1050        job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
1051        edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec));
1052 
1053   if (dcr) {
1054     /*
1055      * Check if we can still write. This may not be the case
1056      *  if we are at the end of the tape or we got a fatal I/O error.
1057      */
1058     if (dcr->dev && dcr->dev->CanWrite()) {
1059       Dmsg1(200, "Write EOS label JobStatus=%c\n", jcr->JobStatus);
1060 
1061       if (!WriteSessionLabel(dcr, EOS_LABEL)) {
1062         /*
1063          * Print only if JobStatus JS_Terminated and not cancelled to avoid
1064          * spurious messages
1065          */
1066         if (jcr->is_JobStatus(JS_Terminated) && !jcr->IsJobCanceled()) {
1067           Jmsg1(jcr, M_FATAL, 0, _("Error writing end session label. ERR=%s\n"),
1068                 dcr->dev->bstrerror());
1069         }
1070         jcr->setJobStatus(JS_ErrorTerminated);
1071       }
1072 
1073       Dmsg0(90, "back from write_end_session_label()\n");
1074 
1075       /*
1076        * Flush out final partial block of this session
1077        */
1078       if (!dcr->WriteBlockToDevice()) {
1079         /*
1080          * Print only if JobStatus JS_Terminated and not cancelled to avoid
1081          * spurious messages
1082          */
1083         if (jcr->is_JobStatus(JS_Terminated) && !jcr->IsJobCanceled()) {
1084           Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
1085                 dcr->dev->print_name(), dcr->dev->bstrerror());
1086         }
1087         jcr->setJobStatus(JS_ErrorTerminated);
1088       }
1089     }
1090 
1091     if (jcr->is_JobStatus(JS_Terminated)) {
1092       /*
1093        * Note: if commit is OK, the device will remain blocked
1094        */
1095       CommitDataSpool(dcr);
1096     } else {
1097       DiscardDataSpool(dcr);
1098     }
1099 
1100     /*
1101      * Release the device -- and send final Vol info to DIR and unlock it.
1102      */
1103     if (jcr->impl->acquired_storage) {
1104       ReleaseDevice(dcr);
1105       jcr->impl->acquired_storage = false;
1106     } else {
1107       dcr->UnreserveDevice();
1108     }
1109   }
1110 
1111   jcr->sendJobStatus(); /* update director */
1112 }
1113 
EndOfNdmpRestore(JobControlRecord * jcr)1114 void EndOfNdmpRestore(JobControlRecord* jcr)
1115 {
1116   if (jcr->impl->read_session.rctx) {
1117     FreeReadContext(jcr->impl->read_session.rctx);
1118     jcr->impl->read_session.rctx = NULL;
1119   }
1120 
1121   if (jcr->impl->acquired_storage) {
1122     ReleaseDevice(jcr->impl->read_dcr);
1123     jcr->impl->acquired_storage = false;
1124   } else {
1125     jcr->impl->read_dcr->UnreserveDevice();
1126   }
1127 }
1128 
HandleNdmpConnectionRequest(ConfigurationParser * config,void * arg)1129 extern "C" void* HandleNdmpConnectionRequest(ConfigurationParser* config,
1130                                              void* arg)
1131 {
1132   int status;
1133   struct ndmconn* conn;
1134   struct ndm_session* sess;
1135   struct ndmp_session_handle* handle;
1136   NIS* nis;
1137 
1138   handle = (struct ndmp_session_handle*)arg;
1139   if (!handle) {
1140     Emsg0(M_ABORT, 0,
1141           _("Illegal call to HandleNdmpConnectionRequest with NULL session "
1142             "handle\n"));
1143     return NULL;
1144   }
1145 
1146   /*
1147    * Initialize a new NDMP session
1148    */
1149   sess = (struct ndm_session*)malloc(sizeof(struct ndm_session));
1150   memset(sess, 0, sizeof(struct ndm_session));
1151 
1152   sess->param
1153       = (struct ndm_session_param*)malloc(sizeof(struct ndm_session_param));
1154   memset(sess->param, 0, sizeof(struct ndm_session_param));
1155   sess->param->log.deliver = storagedaemon::NdmpLoghandler;
1156   nis = (NIS*)malloc(sizeof(NIS));
1157   sess->param->log.ctx = nis;
1158   sess->param->log_level = NativeToNdmpLoglevel(debug_level, nis);
1159   sess->param->log_tag = strdup("SD-NDMP");
1160 
1161   RegisterCallbackHooks(sess);
1162 
1163   /*
1164    * We duplicate some of the code from the ndma server session handling
1165    * available in the NDMJOB NDMP library e.g. we do not enter via
1166    * ndma_daemon_session() and then continue to ndma_server_session() which is
1167    * the normal entry point into the library as the ndma_daemon_session()
1168    * function does things like creating a listen socket, fork and accept the
1169    * connection and the ndma_server_session() function tries to get peername and
1170    * socket names before eventually establishing the NDMP connection. We already
1171    * do all of that ourself via proven code implemented in ndmp_thread_server
1172    * which is calling us.
1173    */
1174 
1175   /*
1176    * Make the ndm_session usable for a new connection e.g. initialize and
1177    * commission.
1178    */
1179   sess->tape_agent_enabled = 1;
1180   sess->data_agent_enabled = 1;
1181 
1182   status = ndma_session_initialize(sess);
1183   if (status) {
1184     Emsg0(M_ABORT, 0, _("Cannot initialize new NDMA session\n"));
1185     goto bail_out;
1186   }
1187 
1188   status = ndma_session_commission(sess);
1189   if (status) {
1190     Emsg0(M_ABORT, 0, _("Cannot commission new NDMA session\n"));
1191     goto bail_out;
1192   }
1193 
1194   conn = ndmconn_initialize(0, (char*)"#C");
1195   if (!conn) {
1196     Emsg0(M_ABORT, 0, _("Cannot initialize new NDMA connection\n"));
1197     goto bail_out;
1198   }
1199 
1200   /*
1201    * Tell the lower levels which socket to use and setup snooping.
1202    */
1203   ndmos_condition_control_socket(sess, handle->fd);
1204   if (me->ndmp_snooping) {
1205     ndmconn_set_snoop(conn, &sess->param->log, sess->param->log_level);
1206   }
1207   ndmconn_accept(conn, handle->fd);
1208 
1209   /*
1210    * Initialize some members now that we have a initialized NDMP connection.
1211    */
1212   conn->call = ndma_call;
1213   conn->context = sess;
1214   sess->plumb.control = conn;
1215   sess->session_handle = handle;
1216 
1217   /*
1218    * This does the actual work e.g. run through the NDMP state machine.
1219    */
1220   while (!conn->chan.eof) { ndma_session_quantum(sess, 1000); }
1221 
1222   /*
1223    * Tear down the NDMP connection.
1224    */
1225   ndmconn_destruct(conn);
1226   ndma_session_decommission(sess);
1227 
1228 bail_out:
1229   UnregisterCallbackHooks(sess);
1230 
1231   free(sess->param->log.ctx);
1232   free(sess->param->log_tag);
1233   free(sess->param);
1234   free(sess);
1235 
1236   close(handle->fd);
1237   if (handle->jcr) FreeJcr(handle->jcr);
1238   if (handle->host) free(handle->host);
1239   free(handle);
1240 
1241   return NULL;
1242 }
1243 
1244 /**
1245  * Create a separate thread that accepts NDMP connections.
1246  * We don't use the Bareos native BnetThreadServerTcp which
1247  * uses the bsock class which is a bit to much overhead
1248  * for simple sockets which we need and has all kinds of
1249  * extra features likes TLS and keep-alive support etc.
1250  * which wouldn't work for NDMP anyhow.
1251  *
1252  * So this is a BnetThreadServerTcp put on a diet which
1253  * also contains the absolute minimum code needed to
1254  * have a robust connection handler.
1255  */
ndmp_thread_server(void * arg)1256 extern "C" void* ndmp_thread_server(void* arg)
1257 {
1258   struct ndmp_thread_server_args* ntsa;
1259   int new_sockfd, status;
1260   socklen_t clilen;
1261   struct sockaddr cli_addr; /* client's address */
1262   int tlog, tmax;
1263   int turnon = 1;
1264   IPADDR *ipaddr, *next;
1265   struct s_sockfd {
1266     dlink link; /* this MUST be the first item */
1267     int fd;
1268     int port;
1269   }* fd_ptr = NULL;
1270   char buf[128];
1271   std::vector<s_sockfd*> sockfds;
1272 #  ifdef HAVE_POLL
1273   nfds_t nfds;
1274   struct pollfd* pfds;
1275 #  endif
1276 
1277   ntsa = (struct ndmp_thread_server_args*)arg;
1278   if (!ntsa) { return NULL; }
1279 
1280   /*
1281    * Remove any duplicate addresses.
1282    */
1283   for (ipaddr = (IPADDR*)ntsa->addr_list->first(); ipaddr;
1284        ipaddr = (IPADDR*)ntsa->addr_list->next(ipaddr)) {
1285     for (next = (IPADDR*)ntsa->addr_list->next(ipaddr); next;
1286          next = (IPADDR*)ntsa->addr_list->next(next)) {
1287       if (ipaddr->GetSockaddrLen() == next->GetSockaddrLen()
1288           && memcmp(ipaddr->get_sockaddr(), next->get_sockaddr(),
1289                     ipaddr->GetSockaddrLen())
1290                  == 0) {
1291         ntsa->addr_list->remove(next);
1292       }
1293     }
1294   }
1295 
1296   char allbuf[256 * 10];
1297   Dmsg1(100, "Addresses %s\n",
1298         BuildAddressesString(ntsa->addr_list, allbuf, sizeof(allbuf)));
1299 
1300 #  ifdef HAVE_POLL
1301   nfds = 0;
1302 #  endif
1303   foreach_dlist (ipaddr, ntsa->addr_list) {
1304     /*
1305      * Allocate on stack from -- no need to free
1306      */
1307     fd_ptr = (s_sockfd*)alloca(sizeof(s_sockfd));
1308     fd_ptr->port = ipaddr->GetPortNetOrder();
1309 
1310     /*
1311      * Open a TCP socket
1312      */
1313     for (tlog = 60;
1314          (fd_ptr->fd = socket(ipaddr->GetFamily(), SOCK_STREAM, 0)) < 0;
1315          tlog -= 10) {
1316       if (tlog <= 0) {
1317         BErrNo be;
1318         char curbuf[256];
1319         Emsg3(M_ABORT, 0,
1320               _("Cannot open stream socket. ERR=%s. Current %s All %s\n"),
1321               be.bstrerror(), ipaddr->build_address_str(curbuf, sizeof(curbuf)),
1322               BuildAddressesString(ntsa->addr_list, allbuf, sizeof(allbuf)));
1323       }
1324       Bmicrosleep(10, 0);
1325     }
1326 
1327     /*
1328      * Reuse old sockets
1329      */
1330     if (setsockopt(fd_ptr->fd, SOL_SOCKET, SO_REUSEADDR, (sockopt_val_t)&turnon,
1331                    sizeof(turnon))
1332         < 0) {
1333       BErrNo be;
1334       Emsg1(M_WARNING, 0, _("Cannot set SO_REUSEADDR on socket: %s\n"),
1335             be.bstrerror());
1336     }
1337 
1338     tmax = 30 * (60 / 5); /* wait 30 minutes max */
1339     for (tlog = 0;
1340          bind(fd_ptr->fd, ipaddr->get_sockaddr(), ipaddr->GetSockaddrLen()) < 0;
1341          tlog -= 5) {
1342       BErrNo be;
1343       if (tlog <= 0) {
1344         tlog = 2 * 60; /* Complain every 2 minutes */
1345         Emsg2(M_WARNING, 0, _("Cannot bind port %d: ERR=%s: Retrying ...\n"),
1346               ntohs(fd_ptr->port), be.bstrerror());
1347       }
1348       Bmicrosleep(5, 0);
1349       if (--tmax <= 0) {
1350         Emsg2(M_ABORT, 0, _("Cannot bind port %d: ERR=%s.\n"),
1351               ntohs(fd_ptr->port), be.bstrerror());
1352       }
1353     }
1354     listen(fd_ptr->fd, me->MaxConnections); /* tell system we are ready */
1355     sockfds.push_back(fd_ptr);
1356 #  ifdef HAVE_POLL
1357     nfds++;
1358 #  endif
1359   }
1360 
1361   ntsa->thread_list->Init(ntsa->max_clients, HandleNdmpConnectionRequest);
1362 
1363 #  ifdef HAVE_POLL
1364   /*
1365    * Allocate on stack from -- no need to free
1366    */
1367   pfds = (struct pollfd*)alloca(sizeof(struct pollfd) * nfds);
1368   memset(pfds, 0, sizeof(struct pollfd) * nfds);
1369 
1370   nfds = 0;
1371   std::for_each(sockfds.begin(), sockfds.end(),
1372                 [&pfds, &nfds](const s_sockfd* fd_ptr) {
1373                   pfds[nfds].fd = fd_ptr->fd;
1374                   pfds[nfds].events |= POLL_IN;
1375                   nfds++;
1376                 });
1377 #  endif
1378 
1379   /*
1380    * Wait for a connection from the client process.
1381    */
1382   while (!quit) {
1383 #  ifndef HAVE_POLL
1384     unsigned int maxfd = 0;
1385     fd_set sockset;
1386     FD_ZERO(&sockset);
1387 
1388     for (auto fd_ptr : sockfds) {
1389       FD_SET((unsigned)fd_ptr->fd, &sockset);
1390       maxfd = maxfd > (unsigned)fd_ptr->fd ? maxfd : fd_ptr->fd;
1391     }
1392 
1393     errno = 0;
1394     if ((status = select(maxfd + 1, &sockset, NULL, NULL, NULL)) < 0) {
1395       BErrNo be; /* capture errno */
1396       if (errno == EINTR) { continue; }
1397       Emsg1(M_FATAL, 0, _("Error in select: %s\n"), be.bstrerror());
1398       break;
1399     }
1400 
1401     for (auto fd_ptr : sockfds) {
1402       if (FD_ISSET(fd_ptr->fd, &sockset)) {
1403 #  else
1404     int cnt;
1405 
1406     errno = 0;
1407     if ((status = poll(pfds, nfds, -1)) < 0) {
1408       BErrNo be; /* capture errno */
1409       if (errno == EINTR) { continue; }
1410       Emsg1(M_FATAL, 0, _("Error in poll: %s\n"), be.bstrerror());
1411       break;
1412     }
1413 
1414     cnt = 0;
1415     for (auto fd_ptr : sockfds) {
1416       if (pfds[cnt++].revents & POLLIN) {
1417 #  endif
1418         /*
1419          * Got a connection, now accept it.
1420          */
1421         do {
1422           clilen = sizeof(cli_addr);
1423           new_sockfd = accept(fd_ptr->fd, &cli_addr, &clilen);
1424         } while (new_sockfd < 0 && errno == EINTR);
1425         if (new_sockfd < 0) { continue; }
1426 
1427         /*
1428          * Receive notification when connection dies.
1429          */
1430         if (setsockopt(new_sockfd, SOL_SOCKET, SO_KEEPALIVE,
1431                        (sockopt_val_t)&turnon, sizeof(turnon))
1432             < 0) {
1433           BErrNo be;
1434           Emsg1(M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"),
1435                 be.bstrerror());
1436         }
1437 
1438         /*
1439          * See who client is. i.e. who connected to us.
1440          */
1441         P(mutex);
1442         SockaddrToAscii(&cli_addr, buf, sizeof(buf));
1443         V(mutex);
1444 
1445         struct ndmp_session_handle* new_handle;
1446         new_handle = (struct ndmp_session_handle*)malloc(
1447             sizeof(struct ndmp_session_handle));
1448         memset(new_handle, 0, sizeof(struct ndmp_session_handle));
1449         new_handle->fd = new_sockfd;
1450         new_handle->host = strdup(buf);
1451         memset(&new_handle->peer_addr, 0, sizeof(new_handle->peer_addr));
1452         memcpy(&new_handle->client_addr, &cli_addr,
1453                sizeof(new_handle->client_addr));
1454 
1455         if (!ntsa->thread_list->CreateAndAddNewThread(my_config, new_handle)) {
1456           Jmsg1(NULL, M_ABORT, 0,
1457                 _("Could not add job to ndmp thread list.\n"));
1458         }
1459       }
1460     }
1461   }
1462 
1463   /*
1464    * Cleanup open files.
1465    */
1466   for (auto fd_ptr : sockfds) {
1467     if (fd_ptr) { close(fd_ptr->fd); }
1468   }
1469 
1470   if (!ntsa->thread_list->ShutdownAndWaitForThreadsToFinish()) {
1471     Emsg1(M_FATAL, 0, _("Could not destroy ndmp thread list.\n"));
1472   }
1473 
1474   return NULL;
1475 }
1476 
1477 int StartNdmpThreadServer(dlist* addr_list, int max_clients)
1478 {
1479   int status;
1480 
1481   ndmp_thread_server_args.addr_list = addr_list;
1482   ndmp_thread_server_args.max_clients = max_clients;
1483   ndmp_thread_server_args.thread_list = &thread_list;
1484 
1485   if ((status = pthread_create(&ndmp_tid, NULL, ndmp_thread_server,
1486                                (void*)&ndmp_thread_server_args))
1487       != 0) {
1488     return status;
1489   }
1490 
1491   ndmp_initialized = true;
1492 
1493   return 0;
1494 }
1495 
1496 void StopNdmpThreadServer()
1497 {
1498   if (!ndmp_initialized) { return; }
1499 
1500   quit = true;
1501   if (!pthread_equal(ndmp_tid, pthread_self())) {
1502     pthread_join(ndmp_tid, NULL);
1503   }
1504 }
1505 #else
1506 void EndOfNdmpBackup(JobControlRecord* jcr) {}
1507 
1508 void EndOfNdmpRestore(JobControlRecord* jcr) {}
1509 #endif /* HAVE_NDMP */
1510 } /* namespace storagedaemon */
1511