Updated logging to include the class/method so that it is more obvious where these...
[ACE_TAO.git] / TAO / tao / Storable_FlatFileStream.cpp
blob08b98a564fe97a67eda1e5aeb12cd8bee95cc685
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 ferror (f2);
99 if (TAO_debug_level > 0)
101 TAOLIB_ERROR ((LM_ERROR,
102 ACE_TEXT ("(%P|%t) TAO::Storable_FlatFileStream, file_copy, f2 handle = %d, %p\n"),
103 ACE_OS::fileno(f2), ACE_TEXT ("ACE_OS::fwrite")));
105 return -1;
108 else
110 errno = 0;
111 if (!feof (f1))
113 ferror (f1);
114 if (TAO_debug_level > 0)
116 TAOLIB_ERROR ((LM_ERROR,
117 ACE_TEXT ("(%P|%t) TAO::Storable_FlatFileStream, file_copy, f1 handle = %d, %p\n"),
118 ACE_OS::fileno(f1), ACE_TEXT ("ACE_OS::fread")));
120 return -1;
124 return 0;
129 TAO::Storable_FlatFileStream::Storable_FlatFileStream (const ACE_CString & file,
130 const char * mode,
131 bool use_backup,
132 bool retry_on_ebadf)
133 : Storable_Base(use_backup, retry_on_ebadf)
134 , filelock_ ()
135 , fl_ (nullptr)
136 , file_(file)
137 , mode_(mode)
139 // filelock_ will be completely initialized in call to init ().
140 filelock_.handle_ = ACE_INVALID_HANDLE;
141 filelock_.lockname_ = nullptr;
144 TAO::Storable_FlatFileStream::~Storable_FlatFileStream ()
146 if ( fl_ != nullptr )
147 this->close();
150 void
151 TAO::Storable_FlatFileStream::do_remove ()
153 ACE_OS::unlink(file_.c_str());
157 TAO::Storable_FlatFileStream::exists ()
159 // We could check the mode for this file, but for now just check exists
160 return ! ACE_OS::access(file_.c_str(), F_OK);
164 TAO::Storable_FlatFileStream::reopen ()
166 if (TAO_debug_level > 0)
168 TAOLIB_DEBUG ((LM_DEBUG,
169 ACE_TEXT ("TAO (%P|%t) Storable_FlatFileStream::reopen, ")
170 ACE_TEXT (" handle = %d\n"),
171 filelock_.handle_));
173 this->close();
174 return this->open();
178 TAO::Storable_FlatFileStream::open()
180 // For now, three flags exist "r", "w", and "c"
181 int flags = 0;
182 const char *fdmode = nullptr;
183 if( ACE_OS::strchr(mode_.c_str(), 'r') )
184 if( ACE_OS::strchr(mode_.c_str(), 'w') )
185 flags = O_RDWR, fdmode = "w+";
186 else
187 flags = O_RDONLY, fdmode = "r";
188 else
189 flags = O_WRONLY, fdmode = "w";
190 if( ACE_OS::strchr(mode_.c_str(), 'c') )
191 flags |= O_CREAT;
193 #ifndef ACE_WIN32
194 if( ACE_OS::flock_init (&filelock_, flags,
195 ACE_TEXT_CHAR_TO_TCHAR (file_.c_str()), 0666) != 0 )
196 TAOLIB_ERROR_RETURN ((LM_ERROR,
197 ACE_TEXT ("(%P|%t) Storable_FFS::open ")
198 ACE_TEXT ("Cannot open file %s for mode %s: %p\n"),
199 file_.c_str(), mode_.c_str(), ACE_TEXT ("ACE_OS::flock_init")),
200 -1);
201 #else
202 if( (filelock_.handle_= ACE_OS::open (file_.c_str(), flags, 0)) == ACE_INVALID_HANDLE )
203 TAOLIB_ERROR_RETURN ((LM_ERROR,
204 ACE_TEXT ("(%P|%t) Storable_FFS::open ")
205 ACE_TEXT ("Cannot open file %s for mode %s: %p\n"),
206 file_.c_str(), mode_.c_str(), ACE_TEXT ("ACE_OS::open")),
207 -1);
208 #endif
210 this->fl_ = nullptr;
211 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
212 attempts > 0 && this->fl_ == nullptr;
213 attempts--)
215 this->fl_ = ACE_OS::fdopen(filelock_.handle_, ACE_TEXT_CHAR_TO_TCHAR (fdmode));
217 if (this->fl_ == nullptr)
219 if (TAO_debug_level > 0)
221 TAOLIB_ERROR ((LM_ERROR,
222 ACE_TEXT ("(%P|%t) Storable_FFS::open ")
223 ACE_TEXT ("Cannot open file %s for mode %s: %p\n"),
224 file_.c_str(), mode_.c_str(), ACE_TEXT ("ACE_OS::fdopen")));
226 if (errno != EBADF)
228 break;
232 return this->fl_ == nullptr ? -1 : 0;
236 TAO::Storable_FlatFileStream::close()
238 ACE_OS::fflush(fl_);
239 #ifndef ACE_WIN32
240 ACE_OS::flock_destroy (&filelock_, 0);
241 #endif
242 ACE_OS::fclose (fl_); // even though flock_destroy closes the handle
243 // we still need to destroy the FILE*
245 fl_ = nullptr;
246 return 0;
250 TAO::Storable_FlatFileStream::flock (int whence, int start, int len)
252 int result = 0;
253 #if defined (ACE_WIN32)
254 ACE_UNUSED_ARG (whence);
255 ACE_UNUSED_ARG (start);
256 ACE_UNUSED_ARG (len);
257 #else
258 bool shared = ACE_OS::strcmp(mode_.c_str(), "r") == 0;
259 result = -1;
260 bool retry = false;
261 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
262 attempts > 0 && result != 0;
263 attempts--)
265 if (retry)
266 this->reopen();
267 retry = true;
268 result = shared ?
269 ACE_OS::flock_rdlock(&filelock_, whence, start, len) :
270 ACE_OS::flock_wrlock(&filelock_, whence, start, len);
271 if (result != 0)
273 if (TAO_debug_level > 0)
275 TAOLIB_ERROR ((LM_ERROR,
276 ACE_TEXT ("TAO (%P|%t) - ")
277 ACE_TEXT ("Storable_FlatFileStream::flock, ")
278 ACE_TEXT ("File %C, %p\n"),
279 file_.c_str (),
280 (shared ? ACE_TEXT("rdlock") : ACE_TEXT("wrlock"))));
282 if (errno != EBADF)
284 break;
289 #endif
290 return result;
294 TAO::Storable_FlatFileStream::funlock (int whence, int start, int len)
296 int result = 0;
297 #if defined (ACE_WIN32)
298 ACE_UNUSED_ARG (whence);
299 ACE_UNUSED_ARG (start);
300 ACE_UNUSED_ARG (len);
301 #else
302 result = -1;
303 bool retry = false;
304 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
305 attempts > 0 && result != 0;
306 attempts--)
308 if (retry)
309 this->reopen();
310 retry = true;
311 result = ACE_OS::flock_unlock(&filelock_, whence, start, len);
312 if (result != 0)
314 if (TAO_debug_level > 0)
316 TAOLIB_ERROR ((LM_ERROR,
317 ACE_TEXT ("TAO (%P|%t) - ")
318 ACE_TEXT ("Storable_FlatFileStream::flock, ")
319 ACE_TEXT ("File %C, %p\n"),
320 file_.c_str (),
321 ACE_TEXT("unlock")));
323 if (errno != EBADF)
325 break;
330 #endif
331 return result;
334 time_t
335 TAO::Storable_FlatFileStream::last_changed()
337 ACE_stat st;
338 int result = 0;
339 bool do_stat = filelock_.handle_ == ACE_INVALID_HANDLE;
340 if (!do_stat)
342 bool retry = false;
343 result = -1;
344 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
345 attempts > 0 && result != 0;
346 attempts--)
348 if (retry)
349 this->reopen();
350 retry = true;
352 result = ACE_OS::fstat(filelock_.handle_, &st);
353 if (result != 0)
355 if (TAO_debug_level > 0)
357 TAOLIB_ERROR ((LM_ERROR,
358 ACE_TEXT ("TAO (%P|%t) - ")
359 ACE_TEXT ("Storable_FlatFileStream::last_changed, ")
360 ACE_TEXT ("File %C, handle %d, %p\n"),
361 file_.c_str (), filelock_.handle_, ACE_TEXT("fstat")));
363 if (errno != EBADF)
365 break;
370 else
372 result = ACE_OS::stat (file_.c_str (), &st);
374 if (result != 0)
376 TAOLIB_ERROR ((LM_ERROR,
377 ACE_TEXT ("TAO (%P|%t) - ")
378 ACE_TEXT ("Storable_FlatFileStream::last_changed, ")
379 ACE_TEXT ("Error getting file information for %C, handle %d, %p\n"),
380 this->file_.c_str(), filelock_.handle_, ACE_TEXT("fstat")));
381 throw Storable_Exception (this->file_);
384 return st.st_mtime;
387 void
388 TAO::Storable_FlatFileStream::rewind ()
390 ACE_OS::rewind(this->fl_);
393 bool
394 TAO::Storable_FlatFileStream::flush ()
396 return ACE_OS::fflush(this->fl_);
400 TAO::Storable_FlatFileStream::sync ()
402 return ACE_OS::fsync (this->filelock_.handle_);
405 TAO::Storable_Base &
406 TAO::Storable_FlatFileStream::operator << (const ACE_CString& str)
408 int n =
409 ACE_OS::fprintf(this->fl_, ACE_SSIZE_T_FORMAT_SPECIFIER_ASCII "\n%s\n",
410 str.length(), str.c_str());
411 if (n < 0)
412 this->throw_on_write_error (badbit);
414 return *this;
417 TAO::Storable_Base &
418 TAO::Storable_FlatFileStream::operator >> (ACE_CString& str)
420 int bufSize = 0;
421 ACE_CString::size_type const max_buf_len =
422 ACE_Numeric_Limits<ACE_CString::size_type>::max ();
423 int const max_int = ACE_Numeric_Limits<int>::max ();
424 switch (fscanf(fl_, "%d\n", &bufSize))
426 case 0:
427 this->throw_on_read_error (badbit);
428 break;
429 case EOF:
430 this->throw_on_read_error (eofbit);
433 if (bufSize < 0
434 || static_cast<ACE_CString::size_type> (bufSize) >= max_buf_len
435 || bufSize == max_int)
437 this->throw_on_read_error (badbit);
440 int const strSize = bufSize + 1; // Account for newline
441 std::unique_ptr<char[]> str_array (new char[strSize]);
442 str_array[0] = '\0';
443 if (ACE_OS::fgets (str_array.get (),
444 strSize,
445 this->fl_) == nullptr
446 && bufSize != 0)
448 this->throw_on_read_error (badbit);
450 str = ACE_CString (str_array.get (), nullptr, false);
453 return *this;
456 TAO::Storable_Base &
457 TAO::Storable_FlatFileStream::operator << (ACE_UINT32 i)
459 int const n =
460 ACE_OS::fprintf (this->fl_, ACE_UINT32_FORMAT_SPECIFIER_ASCII "\n", i);
461 if (n < 0)
462 this->throw_on_write_error (badbit);
463 return *this;
466 TAO::Storable_Base &
467 TAO::Storable_FlatFileStream::operator >> (ACE_UINT32 &i)
469 Storable_State state = this->rdstate ();
470 read_integer (ACE_UINT32_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
471 this->throw_on_read_error (state);
473 return *this;
476 TAO::Storable_Base &
477 TAO::Storable_FlatFileStream::operator << (ACE_UINT64 i)
479 int const n =
480 ACE_OS::fprintf (this->fl_, ACE_UINT64_FORMAT_SPECIFIER_ASCII "\n", i);
481 if (n < 0)
482 this->throw_on_write_error (badbit);
483 return *this;
486 TAO::Storable_Base &
487 TAO::Storable_FlatFileStream::operator >> (ACE_UINT64 &i)
489 Storable_State state = this->rdstate ();
490 read_integer (ACE_UINT64_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
491 this->throw_on_read_error (state);
493 return *this;
496 TAO::Storable_Base &
497 TAO::Storable_FlatFileStream::operator << (ACE_INT32 i)
499 int const n =
500 ACE_OS::fprintf (this->fl_, ACE_INT32_FORMAT_SPECIFIER_ASCII "\n", i);
501 if (n < 0)
502 this->throw_on_write_error (badbit);
503 return *this;
506 TAO::Storable_Base &
507 TAO::Storable_FlatFileStream::operator >> (ACE_INT32 &i)
509 Storable_State state = this->rdstate ();
510 read_integer (ACE_INT32_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
511 this->throw_on_read_error (state);
513 return *this;
516 TAO::Storable_Base &
517 TAO::Storable_FlatFileStream::operator << (ACE_INT64 i)
519 int const n =
520 ACE_OS::fprintf (this->fl_, ACE_INT64_FORMAT_SPECIFIER_ASCII "\n", i);
521 if (n < 0)
522 this->throw_on_write_error (badbit);
523 return *this;
526 TAO::Storable_Base &
527 TAO::Storable_FlatFileStream::operator >> (ACE_INT64 &i)
529 Storable_State state = this->rdstate ();
530 read_integer (ACE_INT64_FORMAT_SPECIFIER_ASCII "\n", i, state, fl_);
531 this->throw_on_read_error (state);
533 return *this;
537 TAO::Storable_Base &
538 TAO::Storable_FlatFileStream::operator << (const TAO_OutputCDR & cdr)
540 unsigned int const length =
541 ACE_Utils::truncate_cast<unsigned int> (cdr.total_length ());
542 *this << length;
543 for (const ACE_Message_Block *i = cdr.begin (); i != nullptr; i = i->cont ())
545 const char *bytes = i->rd_ptr ();
546 size_t const len = i->length ();
547 this->write (len, bytes);
549 return *this;
552 size_t
553 TAO::Storable_FlatFileStream::write (size_t size, const char * bytes)
555 return ACE_OS::fwrite (bytes, size, 1, fl_);
558 size_t
559 TAO::Storable_FlatFileStream::read (size_t size, char * bytes)
561 return ACE_OS::fread (bytes, size, 1, fl_);
564 ACE_CString
565 TAO::Storable_FlatFileStream::backup_file_name ()
567 return file_ + ".bak";
571 TAO::Storable_FlatFileStream::create_backup ()
573 if (this->fl_ == nullptr)
575 return 0;
578 bool retry = false;
579 int result = -1;
580 for (int attempts = this->retry_on_ebadf_ ? 2 : 1;
581 attempts > 0 && result < 0;
582 attempts--)
584 if (retry)
585 this->reopen();
586 retry = true;
587 errno = 0;
588 this->rewind();
589 if (errno != 0)
591 if (errno == EBADF)
593 continue;
595 break;
597 FILE * backup = ACE_OS::fopen (this->backup_file_name ().c_str (), "w");
598 result = file_copy(this->fl_, backup);
599 if (result != 0)
601 if (TAO_debug_level > 0)
603 TAOLIB_ERROR ((LM_ERROR,
604 ACE_TEXT ("TAO: (%P|%t) Storable_FlatFileStream::")
605 ACE_TEXT ("create_backup Unable to create backup ")
606 ACE_TEXT ("of file %s\n"), file_.c_str ()));
608 if (errno != EBADF)
610 ACE_OS::fclose (backup);
611 break;
614 ACE_OS::fclose (backup);
616 return result;
619 void
620 TAO::Storable_FlatFileStream::remove_backup ()
622 ACE_CString backup_name = this->backup_file_name ();
624 if (ACE_OS::access (backup_name.c_str (), F_OK) == 0)
626 ACE_OS::unlink (backup_name.c_str ());
631 TAO::Storable_FlatFileStream::restore_backup ()
633 ACE_CString backup_name = this->backup_file_name ().c_str ();
635 if (ACE_OS::access (backup_name.c_str (), F_OK))
636 return -1;
638 if (ACE_OS::strchr (this->mode_.c_str(),'w') == nullptr)
640 this->mode_ += 'w';
642 this->reopen ();
644 FILE * backup = ACE_OS::fopen (backup_name.c_str (),
645 "r");
646 this->rewind();
647 int result = file_copy(backup, this->fl_);
648 ACE_OS::fclose (backup);
649 this->flush ();
650 this->clear ();
651 return result;
654 void
655 TAO::Storable_FlatFileStream::throw_on_read_error (Storable_State state)
657 this->setstate (state);
659 if (!this->good ())
661 throw Storable_Read_Exception (this->rdstate (), this->file_);
665 void
666 TAO::Storable_FlatFileStream::throw_on_write_error (Storable_State state)
668 this->setstate (state);
670 if (!this->good ())
672 throw Storable_Write_Exception (this->rdstate (), this->file_);
676 //------------------------------------------------
678 TAO::Storable_FlatFileFactory::Storable_FlatFileFactory(const ACE_CString & directory,
679 bool use_backup,
680 bool retry_on_ebadf)
681 : Storable_Factory ()
682 , directory_(directory)
683 , use_backup_(use_backup)
684 , retry_on_ebadf_ (retry_on_ebadf)
688 TAO::Storable_FlatFileFactory::Storable_FlatFileFactory(const ACE_CString & directory,
689 bool use_backup)
690 : Storable_Factory ()
691 , directory_(directory)
692 , use_backup_(use_backup)
693 , retry_on_ebadf_ (Storable_Base::retry_on_ebadf_default)
695 retry_on_ebadf_ = Storable_FlatFileFactory::is_nfs (directory);
698 bool
699 TAO::Storable_FlatFileFactory::is_nfs (const ACE_CString& directory)
701 bool ret = false;
702 #if defined (ACE_HAS_MNTENT)
703 const char *dir = directory.c_str();
704 char rpath [PATH_MAX];
705 if (*dir != '/')
707 rpath[0] = 0;
708 if (ACE_OS::getcwd (rpath, PATH_MAX) == nullptr)
710 if (TAO_debug_level > 0)
712 TAOLIB_ERROR ((LM_ERROR,
713 ACE_TEXT ("TAO (%P|%t) Storable_FFFactory::is_nfs ")
714 ACE_TEXT ("could not get full path, %p\n"),
715 ACE_TEXT ("getcwd")));
717 return ret;
719 size_t rootlen = ACE_OS::strlen(rpath);
720 if ((rootlen + directory.length() +1) > PATH_MAX)
722 if (TAO_debug_level > 0)
724 TAOLIB_ERROR ((LM_ERROR,
725 ACE_TEXT ("TAO (%P|%t) Storable_FFFactory::is_nfs ")
726 ACE_TEXT ("combined root + supplied paths too long:")
727 ACE_TEXT ("%C + / + %C\n"), rpath, dir));
729 return ret;
731 char *pos = rpath + rootlen;
732 *pos++ = '/';
733 ACE_OS::strcpy (pos,directory.c_str());
734 dir = rpath;
736 size_t match = 0;
737 size_t dirlen = ACE_OS::strlen(dir);
738 struct mntent *ent = nullptr;
739 const char *fname = "/etc/mtab";
740 FILE *mt = ::setmntent(fname,"r");
741 if (mt == nullptr)
743 if (TAO_debug_level > 0)
745 TAOLIB_ERROR ((LM_ERROR,
746 ACE_TEXT ("TAO (%P|%t) Storable_FFFactory::is_nfs ")
747 ACE_TEXT ("could not open %C, %p\n"),
748 fname, ACE_TEXT ("setmntent")));
750 return ret;
752 while ((ent = ::getmntent(mt)) != nullptr)
754 size_t len = ACE_OS::strlen(ent->mnt_dir);
756 if (len > dirlen || len < match)
758 continue;
760 if (len >= match &&ACE_OS::strstr (dir, ent->mnt_dir) == dir)
762 match = len;
763 ret = (ACE_OS::strcasecmp (ent->mnt_type, "nfs") == 0);
764 if (len == dirlen)
766 break; // exact match
770 ::endmntent (mt);
771 #else
772 ACE_UNUSED_ARG (directory);
773 #endif /* ACE_HAS_MNTENT */
774 return ret;
777 TAO::Storable_FlatFileFactory::~Storable_FlatFileFactory()
781 const ACE_CString &
782 TAO::Storable_FlatFileFactory::get_directory () const
784 return directory_;
787 TAO::Storable_Base *
788 TAO::Storable_FlatFileFactory::create_stream (const ACE_CString & file,
789 const char * mode,
790 bool )
792 TAO::Storable_Base *stream = nullptr;
793 ACE_CString path = this->directory_ + "/" + file;
794 ACE_NEW_RETURN (stream,
795 TAO::Storable_FlatFileStream(path,
796 mode,
797 this->use_backup_,
798 this->retry_on_ebadf_),
799 nullptr);
800 return stream;
803 TAO_END_VERSIONED_NAMESPACE_DECL