Merge pull request #2301 from sonndinh/remove-dup-reactor-functions
[ACE_TAO.git] / TAO / tao / Storable_FlatFileStream.cpp
bloba54226908a45841469e7e0efd8268ede2315a8e9
2 // -*- C++ -*-
4 //=============================================================================
5 /**
6 * @file Storable_FlatFileStream.cpp
8 * @author Marina Spivak <marina@cs.wustl.edu>
9 * @author Byron Harris <harrisb@ociweb.com>
11 //=============================================================================
13 #include "tao/Storable_FlatFileStream.h"
15 #include "ace/OS_NS_unistd.h"
16 #include "ace/OS_NS_fcntl.h"
17 #include "ace/OS_NS_sys_stat.h"
18 #include "ace/OS_NS_strings.h"
19 #include "ace/Numeric_Limits.h"
20 #include "ace/Truncate.h"
21 #include "tao/debug.h"
23 #if defined (ACE_HAS_MNTENT)
24 #include <mntent.h>
25 #endif /* ACE_HAS_MNTENT */
27 #include <memory>
29 TAO_BEGIN_VERSIONED_NAMESPACE_DECL
31 namespace
33 /// Avoids using fscanf to read an integer as any
34 /// whitespace following the newline will be
35 /// consumed. This could create problems if
36 /// the data that follows the newline is binary.
37 template<typename T>
38 void read_integer(const char * format, T & i,
39 TAO::Storable_Base::Storable_State & state,
40 FILE * fl)
42 char buf[BUFSIZ];
43 char * result = fgets (buf, BUFSIZ, fl);
45 if (result == nullptr)
47 if (feof (fl))
49 state = TAO::Storable_Base::eofbit;
50 return;
52 state = TAO::Storable_Base::badbit;
53 return;
56 /// Consume any starting newline, as fscanf would
57 /// do.
58 if (buf[0] == '\n')
60 result = fgets (buf, BUFSIZ, fl);
63 if (result == nullptr)
65 if (feof (fl))
67 state = TAO::Storable_Base::eofbit;
68 return;
70 state = TAO::Storable_Base::badbit;
71 return;
74 switch (sscanf (buf, format, &i))
76 case 0:
77 state = TAO::Storable_Base::badbit;
78 return;
79 case EOF:
80 state = TAO::Storable_Base::eofbit;
81 return;
85 int file_copy (FILE *f1, FILE *f2)
87 char buffer[BUFSIZ];
88 size_t n_read;
90 while (!feof (f1))
92 n_read =
93 ACE_OS::fread(buffer, 1, sizeof(buffer), f1);
94 if (n_read > 0)
96 if (ACE_OS::fwrite(buffer, 1, n_read, f2) != n_read)
98 if (TAO_debug_level > 0)
100 TAOLIB_ERROR ((LM_ERROR,
101 ACE_TEXT ("(%P|%t) TAO::Storable_FlatFileStream, file_copy, f2 handle = %d, %p\n"),
102 ACE_OS::fileno(f2), ACE_TEXT ("ACE_OS::fwrite")));
104 return -1;
107 else
109 errno = 0;
110 if (!feof (f1))
112 if (TAO_debug_level > 0)
114 TAOLIB_ERROR ((LM_ERROR,
115 ACE_TEXT ("(%P|%t) TAO::Storable_FlatFileStream, file_copy, f1 handle = %d, %p\n"),
116 ACE_OS::fileno(f1), ACE_TEXT ("ACE_OS::fread")));
118 return -1;
122 return 0;
127 TAO::Storable_FlatFileStream::Storable_FlatFileStream (const ACE_CString & file,
128 const char * mode,
129 bool use_backup,
130 bool retry_on_ebadf)
131 : Storable_Base(use_backup, retry_on_ebadf)
132 , filelock_ ()
133 , fl_ (nullptr)
134 , file_(file)
135 , mode_(mode)
137 // filelock_ will be completely initialized in call to init ().
138 filelock_.handle_ = ACE_INVALID_HANDLE;
139 filelock_.lockname_ = nullptr;
142 TAO::Storable_FlatFileStream::~Storable_FlatFileStream ()
144 if ( fl_ != nullptr )
145 this->close();
148 void
149 TAO::Storable_FlatFileStream::do_remove ()
151 ACE_OS::unlink(file_.c_str());
155 TAO::Storable_FlatFileStream::exists ()
157 // We could check the mode for this file, but for now just check exists
158 return ! ACE_OS::access(file_.c_str(), F_OK);
162 TAO::Storable_FlatFileStream::reopen ()
164 if (TAO_debug_level > 0)
166 TAOLIB_DEBUG ((LM_DEBUG,
167 ACE_TEXT ("TAO (%P|%t) Storable_FlatFileStream::reopen, ")
168 ACE_TEXT (" handle = %d\n"),
169 filelock_.handle_));
171 this->close();
172 return this->open();
176 TAO::Storable_FlatFileStream::open()
178 // For now, three flags exist "r", "w", and "c"
179 int flags = 0;
180 const char *fdmode = nullptr;
181 if( ACE_OS::strchr(mode_.c_str(), 'r') )
182 if( ACE_OS::strchr(mode_.c_str(), 'w') )
183 flags = O_RDWR, fdmode = "w+";
184 else
185 flags = O_RDONLY, fdmode = "r";
186 else
187 flags = O_WRONLY, fdmode = "w";
188 if( ACE_OS::strchr(mode_.c_str(), 'c') )
189 flags |= O_CREAT;
191 #ifndef ACE_WIN32
192 if( ACE_OS::flock_init (&filelock_, flags,
193 ACE_TEXT_CHAR_TO_TCHAR (file_.c_str()), 0666) != 0 )
194 TAOLIB_ERROR_RETURN ((LM_ERROR,
195 ACE_TEXT ("(%P|%t) Storable_FFS::open ")
196 ACE_TEXT ("Cannot open file %s for mode %s: %p\n"),
197 file_.c_str(), mode_.c_str(), ACE_TEXT ("ACE_OS::flock_init")),
198 -1);
199 #else
200 if( (filelock_.handle_= ACE_OS::open (file_.c_str(), flags, 0)) == ACE_INVALID_HANDLE )
201 TAOLIB_ERROR_RETURN ((LM_ERROR,
202 ACE_TEXT ("(%P|%t) Storable_FFS::open ")
203 ACE_TEXT ("Cannot open file %s for mode %s: %p\n"),
204 file_.c_str(), mode_.c_str(), ACE_TEXT ("ACE_OS::open")),
205 -1);
206 #endif
208 this->fl_ = nullptr;
209 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
210 attempts > 0 && this->fl_ == nullptr;
211 attempts--)
213 this->fl_ = ACE_OS::fdopen(filelock_.handle_, ACE_TEXT_CHAR_TO_TCHAR (fdmode));
215 if (this->fl_ == nullptr)
217 if (TAO_debug_level > 0)
219 TAOLIB_ERROR ((LM_ERROR,
220 ACE_TEXT ("(%P|%t) Storable_FFS::open ")
221 ACE_TEXT ("Cannot open file %s for mode %s: %p\n"),
222 file_.c_str(), mode_.c_str(), ACE_TEXT ("ACE_OS::fdopen")));
224 if (errno != EBADF)
226 break;
230 return this->fl_ == nullptr ? -1 : 0;
234 TAO::Storable_FlatFileStream::close()
236 ACE_OS::fflush(fl_);
237 #ifndef ACE_WIN32
238 ACE_OS::flock_destroy (&filelock_, 0);
239 #endif
240 ACE_OS::fclose (fl_); // even though flock_destroy closes the handle
241 // we still need to destroy the FILE*
243 fl_ = nullptr;
244 return 0;
248 TAO::Storable_FlatFileStream::flock (int whence, int start, int len)
250 int result = 0;
251 #if defined (ACE_WIN32)
252 ACE_UNUSED_ARG (whence);
253 ACE_UNUSED_ARG (start);
254 ACE_UNUSED_ARG (len);
255 #else
256 bool shared = ACE_OS::strcmp(mode_.c_str(), "r") == 0;
257 result = -1;
258 bool retry = false;
259 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
260 attempts > 0 && result != 0;
261 attempts--)
263 if (retry)
264 this->reopen();
265 retry = true;
266 result = shared ?
267 ACE_OS::flock_rdlock(&filelock_, whence, start, len) :
268 ACE_OS::flock_wrlock(&filelock_, whence, start, len);
269 if (result != 0)
271 if (TAO_debug_level > 0)
273 TAOLIB_ERROR ((LM_ERROR,
274 ACE_TEXT ("TAO (%P|%t) - ")
275 ACE_TEXT ("Storable_FlatFileStream::flock, ")
276 ACE_TEXT ("File %C, %p\n"),
277 file_.c_str (),
278 (shared ? ACE_TEXT("rdlock") : ACE_TEXT("wrlock"))));
280 if (errno != EBADF)
282 break;
287 #endif
288 return result;
292 TAO::Storable_FlatFileStream::funlock (int whence, int start, int len)
294 int result = 0;
295 #if defined (ACE_WIN32)
296 ACE_UNUSED_ARG (whence);
297 ACE_UNUSED_ARG (start);
298 ACE_UNUSED_ARG (len);
299 #else
300 result = -1;
301 bool retry = false;
302 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
303 attempts > 0 && result != 0;
304 attempts--)
306 if (retry)
307 this->reopen();
308 retry = true;
309 result = ACE_OS::flock_unlock(&filelock_, whence, start, len);
310 if (result != 0)
312 if (TAO_debug_level > 0)
314 TAOLIB_ERROR ((LM_ERROR,
315 ACE_TEXT ("TAO (%P|%t) - ")
316 ACE_TEXT ("Storable_FlatFileStream::flock, ")
317 ACE_TEXT ("File %C, %p\n"),
318 file_.c_str (),
319 ACE_TEXT("unlock")));
321 if (errno != EBADF)
323 break;
328 #endif
329 return result;
332 time_t
333 TAO::Storable_FlatFileStream::last_changed()
335 ACE_stat st;
336 int result = 0;
337 bool do_stat = filelock_.handle_ == ACE_INVALID_HANDLE;
338 if (!do_stat)
340 bool retry = false;
341 result = -1;
342 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
343 attempts > 0 && result != 0;
344 attempts--)
346 if (retry)
347 this->reopen();
348 retry = true;
350 result = ACE_OS::fstat(filelock_.handle_, &st);
351 if (result != 0)
353 if (TAO_debug_level > 0)
355 TAOLIB_ERROR ((LM_ERROR,
356 ACE_TEXT ("TAO (%P|%t) - ")
357 ACE_TEXT ("Storable_FlatFileStream::last_changed, ")
358 ACE_TEXT ("File %C, handle %d, %p\n"),
359 file_.c_str (), filelock_.handle_, ACE_TEXT("fstat")));
361 if (errno != EBADF)
363 break;
368 else
370 result = ACE_OS::stat (file_.c_str (), &st);
372 if (result != 0)
374 TAOLIB_ERROR ((LM_ERROR,
375 ACE_TEXT ("TAO (%P|%t) - ")
376 ACE_TEXT ("Storable_FlatFileStream::last_changed, ")
377 ACE_TEXT ("Error getting file information for %C, handle %d, %p\n"),
378 this->file_.c_str(), filelock_.handle_, ACE_TEXT("fstat")));
379 throw Storable_Exception (this->file_);
382 return st.st_mtime;
385 void
386 TAO::Storable_FlatFileStream::rewind ()
388 ACE_OS::rewind(this->fl_);
391 bool
392 TAO::Storable_FlatFileStream::flush ()
394 return ACE_OS::fflush(this->fl_);
398 TAO::Storable_FlatFileStream::sync ()
400 return ACE_OS::fsync (this->filelock_.handle_);
403 TAO::Storable_Base &
404 TAO::Storable_FlatFileStream::operator << (const ACE_CString& str)
406 int n =
407 ACE_OS::fprintf(this->fl_, ACE_SSIZE_T_FORMAT_SPECIFIER_ASCII "\n%s\n",
408 str.length(), str.c_str());
409 if (n < 0)
410 this->throw_on_write_error (badbit);
412 return *this;
415 TAO::Storable_Base &
416 TAO::Storable_FlatFileStream::operator >> (ACE_CString& str)
418 int bufSize = 0;
419 ACE_CString::size_type const max_buf_len =
420 ACE_Numeric_Limits<ACE_CString::size_type>::max ();
421 int const max_int = ACE_Numeric_Limits<int>::max ();
422 switch (fscanf(fl_, "%d\n", &bufSize))
424 case 0:
425 this->throw_on_read_error (badbit);
426 break;
427 case EOF:
428 this->throw_on_read_error (eofbit);
431 if (bufSize < 0
432 || static_cast<ACE_CString::size_type> (bufSize) >= max_buf_len
433 || bufSize == max_int)
435 this->throw_on_read_error (badbit);
438 int const strSize = bufSize + 1; // Account for newline
439 std::unique_ptr<char[]> str_array (new char[strSize]);
440 str_array[0] = '\0';
441 if (ACE_OS::fgets (str_array.get (),
442 strSize,
443 this->fl_) == nullptr
444 && bufSize != 0)
446 this->throw_on_read_error (badbit);
448 str = ACE_CString (str_array.get (), nullptr, false);
451 return *this;
454 TAO::Storable_Base &
455 TAO::Storable_FlatFileStream::operator << (ACE_UINT32 i)
457 int const n =
458 ACE_OS::fprintf (this->fl_, ACE_UINT32_FORMAT_SPECIFIER_ASCII "\n", i);
459 if (n < 0)
460 this->throw_on_write_error (badbit);
461 return *this;
464 TAO::Storable_Base &
465 TAO::Storable_FlatFileStream::operator >> (ACE_UINT32 &i)
467 Storable_State state = this->rdstate ();
468 read_integer (ACE_UINT32_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
469 this->throw_on_read_error (state);
471 return *this;
474 TAO::Storable_Base &
475 TAO::Storable_FlatFileStream::operator << (ACE_UINT64 i)
477 int const n =
478 ACE_OS::fprintf (this->fl_, ACE_UINT64_FORMAT_SPECIFIER_ASCII "\n", i);
479 if (n < 0)
480 this->throw_on_write_error (badbit);
481 return *this;
484 TAO::Storable_Base &
485 TAO::Storable_FlatFileStream::operator >> (ACE_UINT64 &i)
487 Storable_State state = this->rdstate ();
488 read_integer (ACE_UINT64_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
489 this->throw_on_read_error (state);
491 return *this;
494 TAO::Storable_Base &
495 TAO::Storable_FlatFileStream::operator << (ACE_INT32 i)
497 int const n =
498 ACE_OS::fprintf (this->fl_, ACE_INT32_FORMAT_SPECIFIER_ASCII "\n", i);
499 if (n < 0)
500 this->throw_on_write_error (badbit);
501 return *this;
504 TAO::Storable_Base &
505 TAO::Storable_FlatFileStream::operator >> (ACE_INT32 &i)
507 Storable_State state = this->rdstate ();
508 read_integer (ACE_INT32_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
509 this->throw_on_read_error (state);
511 return *this;
514 TAO::Storable_Base &
515 TAO::Storable_FlatFileStream::operator << (ACE_INT64 i)
517 int const n =
518 ACE_OS::fprintf (this->fl_, ACE_INT64_FORMAT_SPECIFIER_ASCII "\n", i);
519 if (n < 0)
520 this->throw_on_write_error (badbit);
521 return *this;
524 TAO::Storable_Base &
525 TAO::Storable_FlatFileStream::operator >> (ACE_INT64 &i)
527 Storable_State state = this->rdstate ();
528 read_integer (ACE_INT64_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
529 this->throw_on_read_error (state);
531 return *this;
535 TAO::Storable_Base &
536 TAO::Storable_FlatFileStream::operator << (const TAO_OutputCDR & cdr)
538 unsigned int const length =
539 ACE_Utils::truncate_cast<unsigned int> (cdr.total_length ());
540 *this << length;
541 for (const ACE_Message_Block *i = cdr.begin (); i != nullptr; i = i->cont ())
543 const char *bytes = i->rd_ptr ();
544 size_t const len = i->length ();
545 this->write (len, bytes);
547 return *this;
550 size_t
551 TAO::Storable_FlatFileStream::write (size_t size, const char * bytes)
553 return ACE_OS::fwrite (bytes, size, 1, fl_);
556 size_t
557 TAO::Storable_FlatFileStream::read (size_t size, char * bytes)
559 return ACE_OS::fread (bytes, size, 1, fl_);
562 ACE_CString
563 TAO::Storable_FlatFileStream::backup_file_name ()
565 return file_ + ".bak";
569 TAO::Storable_FlatFileStream::create_backup ()
571 if (this->fl_ == nullptr)
573 return 0;
576 bool retry = false;
577 int result = -1;
578 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
579 attempts > 0 && result < 0;
580 attempts--)
582 if (retry)
583 this->reopen();
584 retry = true;
585 errno = 0;
586 this->rewind();
587 if (errno != 0)
589 if (errno == EBADF)
591 continue;
593 break;
595 FILE * backup = ACE_OS::fopen (this->backup_file_name ().c_str (), "w");
596 result = file_copy(this->fl_, backup);
597 if (result != 0)
599 if (TAO_debug_level > 0)
601 TAOLIB_ERROR ((LM_ERROR,
602 ACE_TEXT ("TAO: (%P|%t) Storable_FlatFileStream::")
603 ACE_TEXT ("create_backup Unable to create backup ")
604 ACE_TEXT ("of file %s\n"), file_.c_str ()));
606 if (errno != EBADF)
608 ACE_OS::fclose (backup);
609 break;
612 ACE_OS::fclose (backup);
614 return result;
617 void
618 TAO::Storable_FlatFileStream::remove_backup ()
620 ACE_CString backup_name = this->backup_file_name ();
622 if (ACE_OS::access (backup_name.c_str (), F_OK) == 0)
624 ACE_OS::unlink (backup_name.c_str ());
629 TAO::Storable_FlatFileStream::restore_backup ()
631 ACE_CString backup_name = this->backup_file_name ().c_str ();
633 if (ACE_OS::access (backup_name.c_str (), F_OK))
634 return -1;
636 if (ACE_OS::strchr (this->mode_.c_str(),'w') == nullptr)
638 this->mode_ += 'w';
640 this->reopen ();
642 FILE * backup = ACE_OS::fopen (backup_name.c_str (),
643 "r");
644 this->rewind();
645 int result = file_copy(backup, this->fl_);
646 ACE_OS::fclose (backup);
647 this->flush ();
648 this->clear ();
649 return result;
652 void
653 TAO::Storable_FlatFileStream::throw_on_read_error (Storable_State state)
655 this->setstate (state);
657 if (!this->good ())
659 throw Storable_Read_Exception (this->rdstate (), this->file_);
663 void
664 TAO::Storable_FlatFileStream::throw_on_write_error (Storable_State state)
666 this->setstate (state);
668 if (!this->good ())
670 throw Storable_Write_Exception (this->rdstate (), this->file_);
674 //------------------------------------------------
676 TAO::Storable_FlatFileFactory::Storable_FlatFileFactory(const ACE_CString & directory,
677 bool use_backup,
678 bool retry_on_ebadf)
679 : Storable_Factory ()
680 , directory_(directory)
681 , use_backup_(use_backup)
682 , retry_on_ebadf_ (retry_on_ebadf)
686 TAO::Storable_FlatFileFactory::Storable_FlatFileFactory(const ACE_CString & directory,
687 bool use_backup)
688 : Storable_Factory ()
689 , directory_(directory)
690 , use_backup_(use_backup)
691 , retry_on_ebadf_ (Storable_Base::retry_on_ebadf_default)
693 retry_on_ebadf_ = Storable_FlatFileFactory::is_nfs (directory);
696 bool
697 TAO::Storable_FlatFileFactory::is_nfs (const ACE_CString& directory)
699 bool ret = false;
700 #if defined (ACE_HAS_MNTENT)
701 const char *dir = directory.c_str();
702 char rpath [PATH_MAX];
703 if (*dir != '/')
705 rpath[0] = 0;
706 if (ACE_OS::getcwd (rpath, PATH_MAX) == nullptr)
708 if (TAO_debug_level > 0)
710 TAOLIB_ERROR ((LM_ERROR,
711 ACE_TEXT ("TAO (%P|%t) Storable_FFFactory::is_nfs ")
712 ACE_TEXT ("could not get full path, %p\n"),
713 ACE_TEXT ("getcwd")));
715 return ret;
717 size_t rootlen = ACE_OS::strlen(rpath);
718 if ((rootlen + directory.length() +1) > PATH_MAX)
720 if (TAO_debug_level > 0)
722 TAOLIB_ERROR ((LM_ERROR,
723 ACE_TEXT ("TAO (%P|%t) Storable_FFFactory::is_nfs ")
724 ACE_TEXT ("combined root + supplied paths too long:")
725 ACE_TEXT ("%C + / + %C\n"), rpath, dir));
727 return ret;
729 char *pos = rpath + rootlen;
730 *pos++ = '/';
731 ACE_OS::strcpy (pos,directory.c_str());
732 dir = rpath;
734 size_t match = 0;
735 size_t dirlen = ACE_OS::strlen(dir);
736 struct mntent *ent = nullptr;
737 const char *fname = "/etc/mtab";
738 FILE *mt = ::setmntent(fname,"r");
739 if (mt == nullptr)
741 if (TAO_debug_level > 0)
743 TAOLIB_ERROR ((LM_ERROR,
744 ACE_TEXT ("TAO (%P|%t) Storable_FFFactory::is_nfs ")
745 ACE_TEXT ("could not open %C, %p\n"),
746 fname, ACE_TEXT ("setmntent")));
748 return ret;
750 while ((ent = ::getmntent(mt)) != nullptr)
752 size_t len = ACE_OS::strlen(ent->mnt_dir);
754 if (len > dirlen || len < match)
756 continue;
758 if (len >= match &&ACE_OS::strstr (dir, ent->mnt_dir) == dir)
760 match = len;
761 ret = (ACE_OS::strcasecmp (ent->mnt_type, "nfs") == 0);
762 if (len == dirlen)
764 break; // exact match
768 ::endmntent (mt);
769 #else
770 ACE_UNUSED_ARG (directory);
771 #endif /* ACE_HAS_MNTENT */
772 return ret;
775 TAO::Storable_FlatFileFactory::~Storable_FlatFileFactory()
779 const ACE_CString &
780 TAO::Storable_FlatFileFactory::get_directory () const
782 return directory_;
785 TAO::Storable_Base *
786 TAO::Storable_FlatFileFactory::create_stream (const ACE_CString & file,
787 const char * mode,
788 bool )
790 TAO::Storable_Base *stream = nullptr;
791 ACE_CString path = this->directory_ + "/" + file;
792 ACE_NEW_RETURN (stream,
793 TAO::Storable_FlatFileStream(path,
794 mode,
795 this->use_backup_,
796 this->retry_on_ebadf_),
797 nullptr);
798 return stream;
801 TAO_END_VERSIONED_NAMESPACE_DECL