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