Convert ENDLINE refs to eol.
[shibboleth/cpp-sp.git] / shib / ReloadableXMLFile.cpp
1 /*
2  *  Copyright 2001-2005 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* ReloadableXMLFile.cpp - basic implementation of a reloadable XML config file
18
19    Scott Cantor
20    1/6/04
21
22    $History:$
23 */
24
25 #include "internal.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #include <xercesc/framework/LocalFileInputSource.hpp>
31
32 using namespace shibboleth::logging;
33 using namespace shibboleth;
34 using namespace saml;
35 using namespace std;
36
37 ReloadableXMLFileImpl::ReloadableXMLFileImpl(const DOMElement* e) : m_doc(NULL), m_root(e) {}
38
39 ReloadableXMLFileImpl::ReloadableXMLFileImpl(const char* pathname) : m_doc(NULL), m_root(NULL)
40 {
41 #ifdef _DEBUG
42     NDC ndc("ReloadableXMLFileImpl");
43 #endif
44     Category& log=Category::getInstance(SHIB_LOGCAT".ReloadableXMLFileImpl");
45
46     saml::XML::Parser p;
47     try
48     {
49         //static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull};
50         auto_ptr_XMLCh widenit(pathname);
51         LocalFileInputSource src(widenit.get());
52         Wrapper4InputSource dsrc(&src,false);
53         m_doc=p.parse(dsrc);
54         m_root=m_doc->getDocumentElement();
55
56         log.infoStream() << "Loaded and parsed XML file (" << pathname << ")" << logging::eol;
57     }
58     catch (XMLException& e)
59     {
60         auto_ptr_char msg(e.getMessage());
61         log.errorStream() << "Xerces error while opening configuration file (" << pathname << "): " << msg.get() << logging::eol;
62         if (m_doc) {
63             m_doc->release();
64             m_doc=NULL;
65         }
66         throw MalformedException(msg.get());
67     }
68     catch (SAMLException& e)
69     {
70         log.errorStream() << "XML error while parsing configuration file (" << pathname << "): " << e.what() << logging::eol;
71         if (m_doc) {
72             m_doc->release();
73             m_doc=NULL;
74         }
75         throw;
76     }
77 #ifndef _DEBUG
78     catch (...)
79     {
80         log.errorStream() << "Unexpected error while parsing configuration file (" << pathname << ")" << logging::eol;
81         if (m_doc) {
82             m_doc->release();
83             m_doc=NULL;
84         }
85         throw;
86     }
87 #endif
88 }
89
90 ReloadableXMLFileImpl::~ReloadableXMLFileImpl()
91 {
92     if (m_doc) {
93         m_doc->release();
94         m_doc=NULL;
95     }
96 }
97
98 ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e) : m_impl(NULL), m_root(e), m_filestamp(0), m_lock(NULL)
99 {
100     static const XMLCh uri[] = { chLatin_u, chLatin_r, chLatin_i, chNull };
101     const XMLCh* pathname=e->getAttributeNS(NULL,uri);
102     if (pathname && *pathname)
103     {
104         auto_ptr_char temp(pathname);
105         m_source=temp.get();
106
107 #ifdef WIN32
108         struct _stat stat_buf;
109         if (_stat(m_source.c_str(), &stat_buf) == 0)
110 #else
111         struct stat stat_buf;
112         if (stat(m_source.c_str(), &stat_buf) == 0)
113 #endif
114             m_filestamp=stat_buf.st_mtime;
115         m_lock=RWLock::create();
116     }
117 }
118
119 void ReloadableXMLFile::lock()
120 {
121     if (!m_lock)
122         return;
123         
124     m_lock->rdlock();
125
126     // Check if we need to refresh.
127 #ifdef WIN32
128     struct _stat stat_buf;
129     if (_stat(m_source.c_str(), &stat_buf) == 0)
130 #else
131     struct stat stat_buf;
132     if (stat(m_source.c_str(), &stat_buf) == 0)
133 #endif
134     {
135         if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime)
136         {
137             // Elevate lock and recheck.
138             m_lock->unlock();
139             m_lock->wrlock();
140             if (m_filestamp>0 && m_filestamp<stat_buf.st_mtime)
141             {
142                 try
143                 {
144                     // Update the timestamp regardless. No point in repeatedly trying.
145                     m_filestamp=stat_buf.st_mtime;
146                     ReloadableXMLFileImpl* new_config=newImplementation(m_source.c_str(),false);
147                     delete m_impl;
148                     m_impl=new_config;
149                     m_lock->unlock();
150                 }
151                 catch(SAMLException& e)
152                 {
153                     m_lock->unlock();
154                     Category::getInstance(SHIB_LOGCAT".ReloadableXMLFile").error("failed to reload config file, sticking with what we have: %s", e.what());
155                 }
156                 catch(...)
157                 {
158                     m_lock->unlock();
159                     Category::getInstance(SHIB_LOGCAT".ReloadableXMLFile").error("caught an unknown exception, sticking with what we have");
160                 }
161             }
162             else
163             {
164                 m_lock->unlock();
165             }
166             m_lock->rdlock();
167         }
168     }
169 }
170
171 ReloadableXMLFileImpl* ReloadableXMLFile::getImplementation() const
172 {
173     if (!m_impl) {
174         if (m_source.empty())
175             m_impl=newImplementation(saml::XML::getFirstChildElement(m_root));
176         else
177             m_impl=newImplementation(m_source.c_str());
178     }
179     return m_impl;
180 }