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