1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2
3 #include "scope.h"
4 #include "server.h"
5
6 #include "pop.h"
7 #include "imap.h"
8 #include "smtp.h"
9 #include "graph.h"
10
11 #include "tlsthread.h"
12 #include "flag.h"
13 #include "event.h"
14 #include "cache.h"
15 #include "mailbox.h"
16 #include "listener.h"
17 #include "database.h"
18 #include "dbsignal.h"
19 #include "selector.h"
20 #include "managesieve.h"
21 #include "spoolmanager.h"
22 #include "entropy.h"
23 #include "egd.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 #include <stdlib.h> // exit()
29
30 #include <time.h> // time()
31
32
33 /*! \nodoc */
34
35
36 class StartupWatcher
37 : public EventHandler
38 {
39 public:
StartupWatcher()40 StartupWatcher(): EventHandler() {}
execute()41 void execute() {
42 if ( Log::disastersYet() )
43 ::exit( 1 );
44 EventLoop::global()->setStartup( false );
45 }
46 };
47
48
49 class ConnectionObliterator
50 : public EventHandler
51 {
52 public:
ConnectionObliterator()53 ConnectionObliterator()
54 : EventHandler() {
55 (void)new DatabaseSignal( "obliterated", this );
56 }
execute()57 void execute() {
58 List<Connection>::Iterator i( EventLoop::global()->connections() );
59 while ( i ) {
60 Connection * c = i;
61 ++i;
62 if ( !c->hasProperty( Connection::Listens ) &&
63 !c->hasProperty( Connection::Internal ) ) {
64 Scope x( c->log() );
65 log( "The database was obliterated" );
66 c->close();
67 }
68 }
69 EventLoop::freeMemorySoon();
70 Cache::clearAllCaches( true );
71 }
72 };
73
74
75 class ArchiveopteryxEventLoop
76 : public EventLoop
77 {
78 public:
ArchiveopteryxEventLoop()79 ArchiveopteryxEventLoop()
80 : fine( true ), unhappinessStarted( 0 ) {}
81 ~ArchiveopteryxEventLoop();
82
83 void freeMemory();
84
85 bool fine;
86 time_t unhappinessStarted;
87 };
88
89
~ArchiveopteryxEventLoop()90 ArchiveopteryxEventLoop::~ArchiveopteryxEventLoop()
91 {
92 }
93
94
freeMemory()95 void ArchiveopteryxEventLoop::freeMemory()
96 {
97 EventLoop::freeMemory();
98 uint fromOS = Allocator::allocatedFromOS();
99 if ( fromOS < memoryUsage() ) {
100 fine = true;
101 return;
102 }
103 if ( fine ) {
104 unhappinessStarted = ::time( 0 );
105 fine = false;
106 }
107
108 // how long have we been in a faq-inducing state?
109 if ( time( 0 ) < unhappinessStarted + 60 )
110 return;
111
112 // oh no. make the parent replace this process with another.
113 if ( ::fork() > 0 )
114 ::exit( 0 );
115 // the process watcher will notice that the parent fork exited,
116 // and start a replacement. in the child, we shut down fairly
117 // quickly.
118 stop( 60 );
119 }
120
121
122
main(int argc,char * argv[])123 int main( int argc, char *argv[] )
124 {
125 Scope global;
126
127 Server s( "archiveopteryx", argc, argv );
128 EventLoop::setup( new ArchiveopteryxEventLoop );
129 s.setup( Server::Report );
130
131 bool security( Configuration::toggle( Configuration::Security ) );
132 EString root( Configuration::text( Configuration::JailDir ) );
133
134 if ( Configuration::toggle( Configuration::UseSmtp ) ||
135 Configuration::toggle( Configuration::UseLmtp ) )
136 {
137 EString mc( Configuration::text( Configuration::MessageCopy ) );
138 EString mcd( Configuration::text( Configuration::MessageCopyDir ) );
139 if ( mc == "all" || mc == "errors" || mc == "delivered" ) {
140 struct stat st;
141 if ( mcd.isEmpty() )
142 log( "message-copy-directory not set", Log::Disaster );
143 else if ( ::stat( mcd.cstr(), &st ) < 0 || !S_ISDIR( st.st_mode ) )
144 log( "Inaccessible message-copy-directory: " + mcd,
145 Log::Disaster );
146 else if ( security && !mcd.startsWith( root ) )
147 log( "message-copy-directory must be under jail directory " +
148 root, Log::Disaster );
149 }
150 else if ( mc == "none" ) {
151 if ( Configuration::present( Configuration::MessageCopyDir ) )
152 log( "Disregarding message-copy-directory (value " + mcd +
153 ") because message-copy is set to none " );
154 }
155 else {
156 log( "Invalid value for message-copy: " + mc, Log::Disaster );
157 }
158 }
159
160
161 EString sA( Configuration::text( Configuration::SmartHostAddress ) );
162 uint sP( Configuration::scalar( Configuration::SmartHostPort ) );
163
164 if ( Configuration::toggle( Configuration::UseSmtp ) &&
165 Configuration::scalar( Configuration::SmtpPort ) == sP &&
166 ( Configuration::text( Configuration::SmtpAddress ) == sA ||
167 ( Configuration::text( Configuration::SmtpAddress ) == "" &&
168 sA == "127.0.0.1" ) ) )
169 {
170 log( "smarthost-address/port are the same as smtp-address/port",
171 Log::Disaster );
172 }
173
174 if ( Configuration::toggle( Configuration::UseLmtp ) &&
175 Configuration::scalar( Configuration::LmtpPort ) == sP &&
176 ( Configuration::text( Configuration::LmtpAddress ) == sA ||
177 ( Configuration::text( Configuration::LmtpAddress ) == "" &&
178 sA == "127.0.0.1" ) ) )
179 {
180 log( "smarthost-address/port are the same as lmtp-address/port",
181 Log::Disaster );
182 }
183
184 if ( Configuration::toggle( Configuration::UseSmtpSubmit ) &&
185 Configuration::scalar( Configuration::SmtpSubmitPort ) == sP &&
186 ( Configuration::text( Configuration::SmtpSubmitAddress ) == sA ||
187 ( Configuration::text( Configuration::SmtpSubmitAddress ) == "" &&
188 sA == "127.0.0.1" ) ) )
189 {
190 log( "smarthost-address/port are the same as "
191 "smtp-submit-address/port", Log::Disaster );
192 }
193
194
195 EString app =
196 Configuration::text( Configuration::AllowPlaintextPasswords ).lower();
197 if ( !( app == "always" || app == "never" ) )
198 ::log( "Unknown value for allow-plaintext-passwords: " + app,
199 Log::Disaster );
200 if ( app == "never" &&
201 Configuration::toggle( Configuration::UseTls ) == false &&
202 Configuration::toggle( Configuration::AuthCramMd5 ) == false &&
203 Configuration::toggle( Configuration::AuthDigestMd5 ) == false )
204 ::log( "allow-plaintext-passwords is 'never' and use-tls is 'false', "
205 "but only plaintext authentication mechanisms are allowed",
206 Log::Disaster );
207
208 EString apa =
209 Configuration::text( Configuration::AllowPlaintextAccess ).lower();
210 if ( !( apa == "always" || apa == "localhost" || apa == "never" ) )
211 ::log( "Unknown value for allow-plaintext-access: " + apa,
212 Log::Disaster );
213 if ( apa == "never" &&
214 Configuration::toggle( Configuration::UseTls ) == false )
215 ::log( "allow-plaintext-access is 'never', but use-tls is 'false'",
216 Log::Disaster );
217
218 // set up an EGD server for openssl
219 Entropy::setup();
220 EString egd( root );
221 if ( !egd.endsWith( "/" ) )
222 egd.append( "/" );
223 egd.append( "var/run/egd-pool" );
224 (void)new Listener< EntropyProvider >( Endpoint( egd, 0 ), "EGD" );
225 if ( !security ) {
226 struct stat st;
227 if ( stat( "/var/run/edg-pool", &st ) < 0 ) {
228 log( "Security is disabled and /var/run/edg-pool does not exist. "
229 "Creating it just in case openssl wants to access it." );
230 (void)new Listener< EntropyProvider >(
231 Endpoint( "/var/run/edg-pool", 0 ), "EGD(/)" );
232 }
233 }
234 if ( ::chmod( egd.cstr(), 0666 ) < 0 )
235 log( "Could not grant r/w access to EGD socket", Log::Disaster );
236
237 Listener< IMAP >::create(
238 "IMAP", Configuration::toggle( Configuration::UseImap ),
239 Configuration::ImapAddress, Configuration::ImapPort
240 );
241 Listener< IMAPS >::create(
242 "IMAPS", Configuration::toggle( Configuration::UseImaps ),
243 Configuration::ImapsAddress, Configuration::ImapsPort
244 );
245 Listener< POP >::create(
246 "POP3", Configuration::toggle( Configuration::UsePop ),
247 Configuration::PopAddress, Configuration::PopPort
248 );
249 Listener< POPS >::create(
250 "POP3S", Configuration::toggle( Configuration::UsePops ),
251 Configuration::PopsAddress, Configuration::PopsPort
252 );
253 Listener< ManageSieve >::create(
254 "Sieve", Configuration::toggle( Configuration::UseSieve ),
255 Configuration::ManageSieveAddress, Configuration::ManageSievePort
256 );
257 Listener< SMTP >::create(
258 "SMTP", Configuration::toggle( Configuration::UseSmtp ),
259 Configuration::SmtpAddress, Configuration::SmtpPort
260 );
261 Listener< LMTP >::create(
262 "LMTP", Configuration::toggle( Configuration::UseLmtp ),
263 Configuration::LmtpAddress, Configuration::LmtpPort
264 );
265 Listener< SMTPSubmit >::create(
266 "SMTP-Submit", Configuration::toggle( Configuration::UseSmtpSubmit ),
267 Configuration::SmtpSubmitAddress, Configuration::SmtpSubmitPort
268 );
269 Listener< SMTPS >::create(
270 "SMTPS", Configuration::toggle( Configuration::UseSmtps ),
271 Configuration::SmtpsAddress, Configuration::SmtpsPort
272 );
273
274 if ( Configuration::toggle( Configuration::UseTls ) ) {
275 TlsThread::setup();
276 }
277
278 s.setup( Server::LogStartup );
279
280 Listener< GraphDumper >::create(
281 "Statistics", Configuration::toggle( Configuration::UseStatistics ),
282 Configuration::StatisticsAddress, Configuration::StatisticsPort
283 );
284
285 EventLoop::global()->setMemoryUsage(
286 1024 * 1024 * Configuration::scalar( Configuration::MemoryLimit ) );
287
288 s.setup( Server::Finish );
289
290 Database::setup();
291
292 StartupWatcher * w = new StartupWatcher;
293
294 Database::checkSchema( w );
295 if ( security )
296 Database::checkAccess( w );
297 EventLoop::global()->setStartup( true );
298 Mailbox::setup( w );
299
300 SpoolManager::setup();
301 Selector::setup();
302 Flag::setup();
303 IMAP::setup();
304
305 if ( !security )
306 (void)new ConnectionObliterator;
307
308 s.run();
309 }
310