]>
git.ipfire.org Git - people/stevee/guardian.git/blob - guardian.in
4be2b779475c01b8705a8f4f7621a1cd71c3ca5b
2 ###############################################################################
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2015-2016 IPFire Development Team #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 ###############################################################################
28 use Time
::HiRes qw
[ time sleep ];
30 require Guardian
::Base
;
31 require Guardian
::Config
;
32 require Guardian
::Daemon
;
33 require Guardian
::Events
;
34 require Guardian
::Logger
;
35 require Guardian
::Parser
;
36 require Guardian
::Socket
;
40 # Disable warnings of unjoined threads when stopping guardian.
41 no warnings
'threads';
44 my $version ="@PACKAGE_VERSION@";
46 # Get and store the given command line arguments in a hash.
49 &GetOptions
(\
%cmdargs,
56 # Show help / version information.
57 if (defined($cmdargs{"help"})) {
58 print "Guardian $version \n";
59 print "Usage: guardian <optional arguments>\n";
60 print " -c, --config\t\tspecifiy a configuration file other than the default (/etc/guardian/guardian.conf)\n";
61 print " -f, --foreground\turn in the foreground (doesn't fork, any output goes to STDOUT)\n";
62 print " -h, --help\t\tshows this help\n";
63 print " -v, --version\t\tdisplay programm version and exit.\n";
65 } elsif (defined($cmdargs{"version"})) {
66 print "Guardian $version \n";
70 # Check if another instance of guardian is allready running.
71 if (&Guardian
::Daemon
::IsRunning
()) {
72 die "Another instance of Guardian is already running...\n";
75 # Read-in the configuration file and store the settings.
76 # Push the may be given config file argument.
77 my %mainsettings = &Guardian
::Config
::UseConfig
($cmdargs{"config"});
80 my $logger = Guardian
::Logger
->Init(%mainsettings);
81 $logger->Log("debug", "Logger successfully initialized...");
83 # Add the logger object to the mainsettings for passing
85 $mainsettings{Logger
} = $logger;
87 # Redirect perls "die" messages to the logger before exiting.
88 $SIG{__DIE__
} = sub { $logger->Log("err", "@_"); };
90 # Initialize the event handler.
91 my $events = Guardian
::Events
->Init(%mainsettings);
93 # Hash to store the currently monitored files and their configured
95 my %monitored_files = ();
97 # Shared hash between the main process and all threads. It will store the
98 # monitored files and their current file position.
99 my %file_positions :shared
= ();
101 # Create the main queue. It is used to store and process all events which are
102 # reported and enqueued by the worker threads.
103 my $queue :shared
= new Thread
::Queue
or die "Could not create new, empty queue. $!";;
105 # Hash to store all currently running worker objects and their corresponding files.
106 # (Does not include the socket thread)
109 # Variable to store if the workers should pause or compute.
110 my $workers_pause :shared
= 0;
112 # Check if guardian should be daemonized or keep in the foreground.
113 unless (defined($cmdargs{"foreground"})) {
114 # Fork into background.
115 &Guardian
::Daemon
::Daemonize
();
117 # Write PID (process-id).
118 &Guardian
::Daemon
::WritePID
();
121 # Call Init function to initzialize guardian.
124 # Log successfully started process.
125 $logger->Log("info", "Guardian $version successfully started...");
127 # Infinite main loop, which processes all queued events.
129 # Get the amount of elements in our queue.
130 # "undef" will be returned if it is empty.
131 my $current_events = $queue->pending();
133 # If there is at least one element enqued
134 if($current_events > 0) {
135 # Grab the data of the top enqueued event.
136 my $event = $queue->peek();
138 # Log processed event.
139 $logger->Log("debug", "QUEUE - Processed event: $event");
141 # Send event data to the events parser to determine
142 # if any action is required.
143 $events->CheckAction($event);
145 # Drop processed event from queue.
149 # Call RemoveBlocks routine from the Events module to check
150 # if items from the block list can be dropped.
151 $events->RemoveBlocks();
153 # Sleep 50ms to reduce the load of the main process.
160 ## This function contains code which has to be executed while guardian
164 # Setup signal handler.
167 # Setup IPC mechanism via Socket in an own thread.
168 threads
->create(\
&Socket
);
170 # Generate hash of monitored files.
171 %monitored_files = &Guardian
::Base
::GenerateMonitoredFiles
(\
%mainsettings, \
%monitored_files);
173 # Start worker threads.
180 ## This function is responsible for monitoring modifications of the given logfile,
181 ## read them and pass them to the message parser.
183 ## To get file modifications the inotify subsystem of the linux kernel is used.
185 ## In order to prevent from permanently read and keep files opened, or dealing
186 ## with huge logfiles, at initialization time of the worker process, the file will
187 ## be opened once and the cursor position of the end of file (EOF) get stored. When
188 ## reading any newly added lines from the file, we directly can jump to the last
189 ## known position and get these lines. Afterwards, we store the current cursor position
190 ## again, so we can do it in this way over and over again.
192 ## All read lines get stored in an array, which will be passed to the Parser.
194 ## If any response (action) from the parser is received, it will be put into the
195 ## shared event queue.
200 # Obtain the parser name which should be used to parse any
201 # messages of this file.
202 my $parser = $monitored_files{$file};
204 # Signal handler to kill worker.
205 $SIG{'KILL'} = sub { threads
->exit(); };
207 # Create inotify watcher.
208 my $watcher = new Linux
::Inotify2
or die "Could not use inotify. $!";
210 # Monitor the specified file.
211 $watcher->watch("$file", IN_MODIFY
) or die "Could not monitor $file. $!";
213 # Switch watcher into non-blocking mode.
214 $watcher->blocking(0);
216 # Log successfully spawned worker.
217 $logger->Log("debug", "Spawned worker thread for: $file");
221 # Check if the workers should pause or perform their desired work.
222 if ($workers_pause) {
223 # Wait 1 second until the next check.
226 # Check for any events and perform them, if there
228 if ($watcher->read) {
231 # Obtain fileposition from hash.
232 my $fileposition = $file_positions{$file};
235 open (FILE
, $file) or die "Could not open $file. $!";
237 # Seek to the last known position.
238 seek (FILE
, $fileposition, 0);
240 # Get the log message.
241 while (my $line = <FILE
>) {
242 # Remove any newlines.
245 # Add all lines to the message array.
246 push (@message, $line);
251 lock(%file_positions);
253 # Update fileposition.
254 $file_positions{$file} = tell(FILE
);
260 # Send filename and message to the parser,
261 # which will return if any actions have to be performed.
262 my @actions = &Guardian
::Parser
::Parser
("$parser", @message);
264 # Send the action to the main process and put it into
270 # Loop through the actions array, and perform
271 # every single action.
272 foreach my $action (@actions) {
273 # Prevent from enqueuing empty actions.
274 if (defined($action)) {
275 # Put the required action into the queue.
276 $queue->enqueue($action);
281 # Sleep for 100ms until the next round of the loop will start.
291 ## This function uses the Socket module to create and listen to an UNIX socket.
292 ## It automatically accepts all incoming connections and pass the received
293 ## data messages to the the Message_Parser function which is also part of the
296 ## If a valid command has been sent through the socket, the corresponding event
297 ## will be enqueued into the shared event queue.
300 # Create the Server socket by calling the responsible function.
301 my $server = &Guardian
::Socket
::Server
($mainsettings{SocketOwner
});
303 # Log successfull creation of socket.
304 $logger->Log("debug", "Listening to Socket...");
306 # Accept incoming connections from the socket.
307 while (my $connection = $server->accept()) {
308 # Autoflush the socket after the data
310 $connection->autoflush(1);
312 # Gather all data from the connection.
313 while (my $message = <$connection>) {
314 # Remove any newlines.
317 # Log received socket command.
318 $logger->Log("debug", "Socket - Recieved message: $message");
320 # Send the received data message to the
322 my $action = &Guardian
::Socket
::Message_Parser
($message);
324 # If the parser returns to perform an action,
325 # add it to the main event queue.
330 # Enqueue the returned action.
331 $queue->enqueue($action);
338 ## Function for capturing process signals.
340 ## This function captures any sent process signals and will call various
341 ## actions, based on the captured signal.
344 $SIG{INT
} = \
&Shutdown
;
345 $SIG{TERM
} = \
&Shutdown
;
346 $SIG{QUIT
} = \
&Shutdown
;
347 $SIG{HUP
} = \
&Reload
;
348 $SIG{USR1
} = \
&ReloadIgnoreList
;
352 ## Function to start the workers (threads) for all monitored files.
354 ## This function will loop through the hash of monitored files and will
355 ## spawn an own thread based worker for each file. Every created worker will
356 ## be added to the array of running workers.
358 sub StartWorkers
() {
359 # Init/Update hash which contains the cursor position of EOF.
360 %file_positions = &Guardian
::Base
::FilePositions
(\
%monitored_files, \
%file_positions);
362 # Loop through the hash which contains the monitored files and start
363 # a worker thread for each single one.
364 foreach my $file (keys %monitored_files) {
365 # Check if an worker is already running for this file.
366 # If not, start the worker.
367 unless (exists($running_workers{$file})) {
368 $logger->Log("debug", "Starting worker thread for $file");
370 # Create worker thread for the file.
371 $running_workers{$file} = threads
->create(\
&Worker
,$file);
377 ## Function to stop all running workers.
379 ## This function is used to stop all currently running workers and will be
380 ## called when reloading or shutting down guardian.
383 # Loop through all running workers.
384 foreach my $worker (keys %running_workers) {
385 # Determine if the worker should be stopped.
386 # This happens if the file should not be longer monitored.
387 unless(exists($monitored_files{$worker})) {
388 $logger->Log("debug", "Stopping worker thread for $worker");
390 # Send a "KILL" signal to the worker.
391 $running_workers{$worker}->kill('KILL');
393 # Remove worker from hash of running workers.
394 delete($running_workers{$worker});
398 # Get amount of currently running worker threads.
399 if (! keys(%running_workers)) {
400 $logger->Log("debug", "All workers have been stopped...");
408 ## Function to pause all running workers.
410 ## This function is used to pause all currently running workers.
413 # Set workers_pause variable to "1".
414 # All workers will sleep until the variable has been set to "0".
417 # Log paused workers.
418 $logger->Log("debug", "All workers have been paused...");
425 ## Function to continue all running workers.
427 ## This function is used to continue all paused workers.
429 sub ResumeWorkers
() {
430 # Set workers_suspend variable to "0" - they will continue their work
434 # Log continued workers.
435 $logger->Log("debug", "All workers are working again...");
444 ## This function will get called if the signal handler receives a "SIGHUP" signal,
445 ## or the reload command will be sent via socket connection. It is responsible for
446 ## reloading all configure options and stopping/starting the worker threads.
450 $logger->Log("info", "Reload configuration...");
452 # Pause all running workers.
455 # Re-read configuration file.
456 %mainsettings = &Guardian
::Config
::UseConfig
($cmdargs{"config"});
458 # Update Logger settings.
459 $logger = Guardian
::Logger
->Init(%mainsettings);
461 # Update logger object in mainsettings hash.
462 $mainsettings{Logger
} = $logger;
464 # Update Event handler.
465 $events->Update(\
%mainsettings);
467 # Update ignore list.
470 # Re-generate hash of monitored files.
471 %monitored_files = &Guardian
::Base
::GenerateMonitoredFiles
(\
%mainsettings, \
%monitored_files);
473 # Stop workers if they are not needed anymore.
476 # Start new worker threads if required.
487 ## ReloadIgnoreList function.
489 ## This function will be called if the signal handler receives a "SIGUSR1" signal,
490 ## or the reload-ignore-list command will be sent via the socket connection. It just
491 ## performs a simple check if an ignore file has been configured and calls the responsible
492 ## function on the events module.
494 sub ReloadIgnoreList
() {
495 # Update ignore list, if an ignorefile has been specified.
496 if (exists($mainsettings{IgnoreFile
})) {
497 # Log reload of the ignore list.
498 $logger->Log("info", "Reloading ignore list...");
500 # Call responsible function from the events module.
501 &Guardian
::Events
::GenerateIgnoreList
($mainsettings{IgnoreFile
});
506 ## Logrotate function.
508 ## This function only get called when the logrotate command will be sent via
509 ## the socket connection. It is responsible for validating and altering the current
510 ## cursor positions of the monitored files and stopping/starting the worker threads.
513 # Stop all running workers.
518 lock(%file_positions);
520 # Loop through the hash which contains the current
522 foreach my $file (keys(%file_positions)) {
523 # Obtain stored value from hash.
524 my $stored_position = $file_positions{$file};
526 # Call function to get the current position.
527 my $current_position = &Guardian
::Base
::GetFileposition
($file);
529 # Compare if the current position still matches
531 if ($current_position ne $stored_position) {
532 # Update to the new position, because
533 # they has changed during file rotation.
534 $file_positions{$file} = $current_position;
536 # Log notice about rotated file.
537 $logger->Log("debug", "$file have been rotated - Using new file position.");
541 # After this bracket, the lock of the hash will be released.
544 # Restart all worker threads.
552 ## Shutdown function.
554 ## This function is used to do a clean shutdown of guardian. It will be called
555 ## by the signal handler when receiving INT (2), QUIT (3) and TERM (15) signals.
559 $logger->Log("info", "Shutting down...");
561 # Reset hash of monitored files.
562 %monitored_files = ();
567 # Unblock all blocked hosts.
568 &Guardian
::Events
::CallFlush
();
570 # Remove socket file on exit.
571 &Guardian
::Socket
::RemoveSocketFile
();
573 # Remove pid file on exit.
574 &Guardian
::Daemon
::RemovePIDFile
();
576 # Sleep for one second to give perl some
577 # time to proper clean up everything before
581 # Log good bye message.
582 $logger->Log("debug", "Good Bye!");