PageRenderTime 61ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/syslog2loggly.pl

https://github.com/xme/syslog2loggly
Perl | 209 lines | 159 code | 18 blank | 32 comment | 30 complexity | ace521de8813f25c4b80e94628bb545f MD5 | raw file
  1. #!/usr/bin/perl
  2. #
  3. # syslog2loggly.pl
  4. # Forward received syslog events to a Loggly Input using HTTPS
  5. #
  6. # Usage: syslog2loggly.pl [-D] [-h] [-p port] [-v] [-f keyfile]
  7. #
  8. # Default configuration file: /etc/syslog2loggly.cong
  9. # apikey=xxxxx
  10. # port=xxxxx
  11. #
  12. # Original script: http://wiki.nil.com/Syslog_Server_in_Perl
  13. #
  14. # Note:
  15. # LWP will support https URLs if the Crypt::SSLeay module is installed.
  16. #
  17. use strict;
  18. use Getopt::Std;
  19. use HTTP::Request::Common qw(POST);
  20. use LWP::UserAgent;
  21. use Net::SSLGlue;
  22. use IO::Socket;
  23. use POSIX qw(setsid);
  24. # We don't care about ended child (to avoid zombies)
  25. $SIG{CHLD} = "IGNORE";
  26. # Variables and Constants
  27. my $MAXLEN = 1524;
  28. my @facilities = ("kernel", "user", "mail", "daemon", "auth", "syslog", "lpr",
  29. "news", "uucp", "cron", "authpriv", "ftp", "local0", "local1",
  30. "local2", "local3", "local4", "local5", "local6", "local7");
  31. my @severities = ("emergency", "alert", "critical", "error", "warning", "notice",
  32. "info", "debug");
  33. my $buf;
  34. my $pid;
  35. my $rin = '';
  36. my $daemon = 0;
  37. my $verbose = 0;
  38. my $port = 5140;
  39. my $apikey = '';
  40. my $config = "/etc/syslog2loggly.conf";
  41. my %opt = ();
  42. # Process arguments
  43. getopts('Dhvp:f:', \%opt);
  44. if (defined($opt{D})) { $daemon++; }
  45. if (defined($opt{h})) { usage(); }
  46. if (defined($opt{v})) { $verbose++; }
  47. if (defined($opt{p})) { $port = $opt{p}; }
  48. if (defined($opt{f})) { $config = $opt{f}; }
  49. # Read the config file
  50. if (! -r $config) {
  51. print STDERR "ERROR: Cannot read the configuration file $config.\n";
  52. exit 1;
  53. }
  54. else {
  55. open(CONF, "$config");
  56. while(<CONF>)
  57. {
  58. chomp($_);
  59. $_=~s/\s//g;
  60. my ($keyword, $value) = split("=", $_);
  61. $keyword =~ tr/A-Z/a-z/;
  62. SWITCH: {
  63. if ($keyword eq "apikey") {
  64. $apikey = $value;
  65. last SWITCH;
  66. }
  67. if ($keyword eq "port") {
  68. $port = $value;
  69. last SWITCH;
  70. }
  71. }
  72. }
  73. close(CONF);
  74. }
  75. if (length($apikey) != 36) {
  76. print STDERR "ERROR: Invalid or not found API key. Check loggly.com.\n";
  77. exit 1;
  78. }
  79. if ($port < 1 || $port > 65535)
  80. {
  81. print STDERR "ERROR: Invalid port number: $port.\n";
  82. exit 1;
  83. }
  84. if ($daemon)
  85. {
  86. if ($verbose)
  87. {
  88. print STDERR "ERROR: -D and -v options are mutually exclusive.\n";
  89. exit 1;
  90. }
  91. # Detach us
  92. if (!defined($pid = fork))
  93. {
  94. print STDERR "ERROR: Cannot fork. Aborted.\n";
  95. exit 1;
  96. }
  97. exit if $pid;
  98. if (POSIX::setsid == -1)
  99. {
  100. print STDERR "ERROR: setsid: $!\n";
  101. exit 1;
  102. }
  103. if (!chdir("/tmp")) {
  104. print STDERR "ERROR: Cannot chdir to /tmp.\n";
  105. exit 1;
  106. }
  107. close(STDERR);
  108. close(STDOUT);
  109. close(STDIN);
  110. }
  111. # Start Listening on UDP port the given port (default 54100)
  112. ($verbose) && print STDERR "Binding to port $port\n";
  113. my $sock = IO::Socket::INET->new(LocalPort => $port, Proto => 'udp')||die("Socket: $@");
  114. ($verbose) && print STDERR "Ready to accept events\n";
  115. # ---------
  116. # Main loop
  117. # ---------
  118. while(1)
  119. {
  120. $sock->recv($buf, $MAXLEN);
  121. my ($port, $ipaddr) = sockaddr_in($sock->peername);
  122. my $hn = gethostbyaddr($ipaddr, AF_INET);
  123. $buf=~/<(\d+)>(.*)/;
  124. my $pri=$1;
  125. my $msg=$2;
  126. my $sev=$pri % 8;
  127. my $fac=($pri-$sev) / 8;
  128. # Fork ourself to process the received event
  129. $pid = fork;
  130. if ($pid == -1)
  131. {
  132. print STDERR "ERROR: Cannot fork. Aborting.\n";
  133. exit 1;
  134. } elsif ($pid) {
  135. # We are the parent
  136. next;
  137. }
  138. else {
  139. # We are the child
  140. logsys($fac,$sev,$msg);
  141. exit(0);
  142. }
  143. }
  144. # Logs Syslog messages
  145. sub logsys
  146. {
  147. my $facility=shift;
  148. my $severity=shift;
  149. my $msg=shift;
  150. my $maxretries = 3;
  151. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  152. $year+=1900; $mon++;
  153. $msg=~/.*Location: \(.*\) (\d+.\d+.\d+.\d+)/;
  154. my $ip = $1;
  155. my $newmsg = sprintf "%04d-%02d-%02dT%02d:%02d:%02d %s facility=%s,severity=%s %s\n", $year, $mon, $mday, $hour, $min, $sec, $ip, $facilities[$facility], $severities[$severity], $msg;
  156. ($verbose) && print STDERR "Message: $newmsg\n";
  157. while ($maxretries > 0)
  158. {
  159. # Post the event to Loggly
  160. my $ua = LWP::UserAgent->new(
  161. agent => 'syslog2loggly.pl');
  162. my $url = "https://logs.loggly.com/inputs/$apikey";
  163. my $req = POST $url,
  164. Content_Type => 'text/plain',
  165. Content => $newmsg;
  166. my $res = $ua->request($req);
  167. # Template: {"response":"ok"}
  168. $res->decoded_content=~/\{\"response\":\"(.*)\"\}/;
  169. if ($1 != "ok") {
  170. print STDERR "Cannot post event (PID $$). Retrying ...\n";
  171. sleep(15);
  172. $maxretries--;
  173. }
  174. else {
  175. # Event successfully sent
  176. $maxretries=0;
  177. }
  178. }
  179. }
  180. sub usage()
  181. {
  182. print STDERR <<EOF;
  183. syslog2loggly.pl [-f keyfile] [-D] [-h] [-v] [-p port]
  184. -D : Run as a daemon
  185. -h : This help
  186. -f keyfile : Configuration file (default: /etc/syslog2loggly.conf)
  187. -p port : Bind to port (default 5140)
  188. -v : Increase verbosity
  189. EOF
  190. exit 1;
  191. }