perl скрипт обработчика логов postfix

И всё прочее, что касается HTML
Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
Гость
проходил мимо

perl скрипт обработчика логов postfix

Непрочитанное сообщение Гость » 2010-03-17 13:45:22

Здравствуйте господа программисты.
Настроил по этой статье http://www.lissyara.su/articles/freebsd ... tatistics/. Вместе с тем мне надо, чтобы в БД заносился статус письма (отправлено ли, доставлено ли в ящик юзеру, не отправилось ли и т.п.).
С перлом столкнулся впервые, накачал книг, но изучение займет продолжительное время пока сам смогу сделать.
Скрипт из той статьи:

Код: Выделить всё

cat maillog.pl
#!/usr/local/bin/perl
use DBI;

$dbh = DBI->connect("DBI:mysql:host=localhost;database=maillogs","mailuser","mailuser")
    or die "Нет доступа к СУБД!";
#$insert = "INSERT INTO mails (id,year,month,day,time,ip,mailfrom,rcptto,size) VALUES(?,?,?,?,?,?,?,?,?)";
$insert = "INSERT INTO mails (id,year,month,day,time,ip,mailfrom,rcptto,size,status) VALUES(?,?,?,?,?,?,?,?,?,?)";
$sth = $dbh->prepare("$insert");

my %rec;

open(MAIL, "/var/log/maillog");
while ($line = <MAIL>)
{
  my ($month, $day, $time, $hostname, $servicename, $id, $message) = split /\s+/, $line, 7;
  if ($id =~ /([a-z0-9]+)\:/i)
  {
    $id = $1;
    $rec{$id} = {}
      unless ($rec{$id});

    if ($message =~ 'removed')
    {
      $rec{$id}->{'removed'}++;
    }
    else
    {
#      while ($message =~ /(client|size|from|to)=(\S+?)(\s|,)/g)
       while ($message =~ /(client|size|from|to|status)=(\S+?)(\s|,)/g)
      по аналогии добавил status - в бд теперь заносится при любых вариантах слово sent
      {
        if ($1 eq 'client') {
        $rec{$id}->{'month'} = sprintf "%s", $month;
        $rec{$id}->{'day'} = sprintf "%d", $day;
        $rec{$id}->{'time'} = sprintf "%s", $time;
        }
        $rec{$id}->{$1} = $2;
      }
    }
  }
}
close(MAIL);

($year) = (localtime)[5];

$timy = ("%02d\n", $year + 1900);

foreach my $id (sort { $rec{$a}->{'time'} cmp $rec{$b}->{'time'} } keys %rec)
{
  $rec{$id}->{'client'} =~ s/(.+)\[(\d+\.\d+\.\d+\.\d+)\]/$2/;
  $rec{$id}->{'from'} =~ s/<(.+)>/$1/;
  $rec{$id}->{'to'} =~ s/<(.+)>/$1/;
  if (
      $rec{$id}->{'removed'}
      &&
      $rec{$id}->{'client'} ne '127.0.0.1'
      )
  {
  if ($rec{$id}->{'from'} ne 'root@fortero.su')
        {
        $sth1 = $dbh->selectrow_arrayref("SELECT COUNT(*) FROM mails");
        $iddt = "$sth1->[0]";
        $idd = $iddt + 1;
#       $sth->execute($idd,$timy,$rec{$id}->{'month'},$rec{$id}->{'day'},$rec{$id}->{'time'},$rec{$id}->{'client'},$rec{$id}->{'from'},$rec{$id}->{'to'},$rec{$id}->{'size'});
        $sth->execute($idd,$timy,$rec{$id}->{'month'},$rec{$id}->{'day'},$rec{$id}->{'time'},$rec{$id}->{'client'},$rec{$id}->{'from'},$rec{$id}->{'to'},$rec{$id}->{'size'},$rec{$id}->{'status'});
        }
  }
}

$sth1->finish;
$sth->finish;
$dbh->disconnect;
В другой статье вычитал принцип работы скрипта, но не могу понять как добавить, то что с письмом в итоге стало.
Помогите, пожалуйста, разобраться.

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/

Аватара пользователя
ProFTP
подполковник
Сообщения: 3388
Зарегистрирован: 2008-04-13 1:50:04
Откуда: %&й
Контактная информация:

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение ProFTP » 2010-03-17 14:07:31

ну вы можете вставить полный текст то что написал постфик $line в стобец mysql...

как-то так:

Код: Выделить всё

$rec{$id}->{full} = $line;
тут добавить столбец

Код: Выделить всё

$insert = "INSERT INTO mails (id,year,month,day,time,ip,mailfrom,rcptto,size,status) VALUES(?,?,?,?,?,?,?,?,?,?)";
и тут написать, добавить $rec{$id}->{full}

Код: Выделить всё

        $sth->execute($idd,$timy,$rec{$id}->{'month'},$rec{$id}->{'day'},$rec{$id}->{'time'},$rec{$id}->{'client'},$rec{$id}->{'from'},$rec{$id}->{'to'},$rec{$id}->{'size'},$rec{$id}->{'status'});
Pеrl FAQ
perl -e 'print join"",map $$_[rand@$_],([0..9,'a'..'z','A'..'Z'])x30'
ИзображениеИзображение


Аватара пользователя
ProFTP
подполковник
Сообщения: 3388
Зарегистрирован: 2008-04-13 1:50:04
Откуда: %&й
Контактная информация:

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение ProFTP » 2010-03-18 9:44:11

оператор срванения, вправой части как правило ставят регулярное выражение... (замена и т.д.)
Pеrl FAQ
perl -e 'print join"",map $$_[rand@$_],([0..9,'a'..'z','A'..'Z'])x30'
ИзображениеИзображение

Гость
проходил мимо

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение Гость » 2010-03-18 15:05:53

спасибо, понятно.
В столбец status заводится теперь первое совпадение по слову status.
Что примерно надо написать, чтобы в бд заводилось второе совпавшееся слово sent для такого же id message?
для пояснения:

Код: Выделить всё

Mar 18 09:42:09 mail postfix/smtp[49119]: 23474444A0D: to=<admin@utk.su>, relay=127.0.0.1[127.0.0.1]:10025, delay=0.31, delays=0.03/0.02/0.14/0.12, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 53BCD444A0E)

Mar 18 09:42:09 mail postfix/virtual[49122]: 53BCD444A0E: to=<admin@utk.su>, relay=virtual, delay=0.14, delays=0.11/0.02/0/0.01, dsn=2.0.0, status=sent (delivered to maildir)
первое совпадение малоинформативно, зато второе уже более полезно

Аватара пользователя
ProFTP
подполковник
Сообщения: 3388
Зарегистрирован: 2008-04-13 1:50:04
Откуда: %&й
Контактная информация:

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение ProFTP » 2010-03-18 15:17:55

надо сомтреть, я бы создал столбцы sent и id, занес бы туда даные и делал бы выборку...
можно еще может как-то поиском в mysql помощю LIKE или FULLTEXT
Pеrl FAQ
perl -e 'print join"",map $$_[rand@$_],([0..9,'a'..'z','A'..'Z'])x30'
ИзображениеИзображение


zg
полковник
Сообщения: 5845
Зарегистрирован: 2007-12-07 13:51:33
Откуда: Верх-Нейвинск

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение zg » 2010-03-22 20:41:40

Гость писал(а):не могу найти как расшифровать regexp:
как мне видится - заменить превое входение НЕпереводов строки в угловых скобках, на тоже самое вхождение без угловых скобок.

Гость
проходил мимо

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение Гость » 2010-03-25 9:07:38

Надо обработать реджект в постфиксе и занести в таблицу mysql
/var/log/maillog:

Код: Выделить всё

Mar 24 10:31:00 mail postfix/smtpd[85933]: NOQUEUE: reject: RCPT from unknown[10.0.33.99]: 450 4.7.1 <123.su>: Helo command rejected: Host not found; from=<root@123.su> to=<root@123.su> proto=ESMTP helo=<123.su>
Выдержка из скрипта:

Код: Выделить всё

my ($month, $day, $time, $hostname, $servicename, $trash, $code, $message) = split /\s+/ , $line, 8;

if ( $code eq 'reject:' ){

       my %arr = split /[\[\]<>]/, $message; #Разбивка по любому из 4-х символов: [ ] < >
       foreach my $i (keys %arr)
               {
            print "$i";        #вывод ключей хеша
             print "$arr{$i}"; #вывод значений хеша
 #теперь как-то надо данные из хеша занести в БД
             $dbh->do("INSERT INTO `reject` (`ip`,`mailfrom`,`rcptto`) VALUES(;
               }
       print "\n";
       }
}
Помогите или подскажите принцип как вытащить значения из хеша и правильно передать в mysql базу?

Аватара пользователя
ProFTP
подполковник
Сообщения: 3388
Зарегистрирован: 2008-04-13 1:50:04
Откуда: %&й
Контактная информация:

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение ProFTP » 2010-03-25 14:02:47

Код: Выделить всё

if ( $code eq 'reject:' ){

       my %arr = split /[\[\]<>]/, $message; #Разбивка по любому из 4-х символов: [ ] < >
       foreach my $i (keys %arr)
               {
            print "$i";        #вывод ключей хеша
             print "$arr{$i}"; #вывод значений хеша
 #теперь как-то надо данные из хеша занести в БД
             $dbh->do("INSERT INTO `reject` (`ip`,`mailfrom`,`rcptto`) VALUES(?,?,?), undef,  $arr{$i} );
               }
       print "\n";
       }
}
ну если так как вы задумали то так...
т.е. каждый ключ нужно вставить сюда $dbh->do("INSERT INTO `reject` (`ip`,`mailfrom`,`rcptto`) VALUES(?,?,?), undef, $arr{$i} );


можно вставить и одним запросом:
http://search.cpan.org/~timb/DBI-1.609/ ... aram_array
bind_param_array

$rc = $sth->bind_param_array($p_num, $array_ref_or_value)
$rc = $sth->bind_param_array($p_num, $array_ref_or_value, \%attr)
$rc = $sth->bind_param_array($p_num, $array_ref_or_value, $bind_type)

The bind_param_array method is used to bind an array of values to a placeholder embedded in the prepared statement which is to be executed with "execute_array". For example:

$dbh->{RaiseError} = 1; # save having to check each method call
$sth = $dbh->prepare("INSERT INTO staff (first_name, last_name, dept) VALUES(?, ?, ?)");
$sth->bind_param_array(1, [ 'John', 'Mary', 'Tim' ]);
$sth->bind_param_array(2, [ 'Booth', 'Todd', 'Robinson' ]);
$sth->bind_param_array(3, "SALES"); # scalar will be reused for each row
$sth->execute_array( { ArrayTupleStatus => \my @tuple_status } );

The %attr ($bind_type) argument is the same as defined for "bind_param". Refer to "bind_param" for general details on using placeholders.

(Note that bind_param_array() can not be used to expand a placeholder into a list of values for a statement like "SELECT foo WHERE bar IN (?)". A placeholder can only ever represent one value per execution.)

Scalar values, including undef, may also be bound by bind_param_array. In which case the same value will be used for each "execute" call. Driver-specific implementations may behave differently, e.g., when binding to a stored procedure call, some databases may permit mixing scalars and arrays as arguments.

The default implementation provided by DBI (for drivers that have not implemented array binding) is to iteratively call "execute" for each parameter tuple provided in the bound arrays. Drivers may provide more optimized implementations using whatever bulk operation support the database API provides. The default driver behaviour should match the default DBI behaviour, but always consult your driver documentation as there may be driver specific issues to consider.

Note that the default implementation currently only supports non-data returning statements (INSERT, UPDATE, but not SELECT). Also, bind_param_array and "bind_param" cannot be mixed in the same statement execution, and bind_param_array must be used with "execute_array"; using bind_param_array will have no effect for "execute".

The bind_param_array method was added in DBI 1.22.
http://search.cpan.org/~timb/DBI-1.609/ ... cute_array
execute_array

$tuples = $sth->execute_array(\%attr) or die $sth->errstr;
$tuples = $sth->execute_array(\%attr, @bind_values) or die $sth->errstr;

($tuples, $rows) = $sth->execute_array(\%attr) or die $sth->errstr;
($tuples, $rows) = $sth->execute_array(\%attr, @bind_values) or die $sth->errstr;

Execute the prepared statement once for each parameter tuple (group of values) provided either in the @bind_values, or by prior calls to "bind_param_array", or via a reference passed in \%attr.

When called in scalar context the execute_array() method returns the number of tuples executed, or undef if an error occurred. Like execute(), a successful execute_array() always returns true regardless of the number of tuples executed, even if it's zero. If there were any errors the ArrayTupleStatus array can be used to discover which tuples failed and with what errors.

When called in list context the execute_array() method returns two scalars; $tuples is the same as calling execute_array() in scalar context and $rows is the sum of the number of rows affected for each tuple, if available or -1 if the driver cannot determine this. If you are doing an update operation the returned rows affected may not be what you expect if, for instance, one or more of the tuples affected the same row multiple times. Some drivers may not yet support list context, in which case $rows will be undef, or may not be able to provide the number of rows affected when performing this batch operation, in which case $rows will be -1.

Bind values for the tuples to be executed may be supplied row-wise by an ArrayTupleFetch attribute, or else column-wise in the @bind_values argument, or else column-wise by prior calls to "bind_param_array".

Where column-wise binding is used (via the @bind_values argument or calls to bind_param_array()) the maximum number of elements in any one of the bound value arrays determines the number of tuples executed. Placeholders with fewer values in their parameter arrays are treated as if padded with undef (NULL) values.

If a scalar value is bound, instead of an array reference, it is treated as a variable length array with all elements having the same value. It's does not influence the number of tuples executed, so if all bound arrays have zero elements then zero tuples will be executed. If all bound values are scalars then one tuple will be executed, making execute_array() act just like execute().

The ArrayTupleFetch attribute can be used to specify a reference to a subroutine that will be called to provide the bind values for each tuple execution. The subroutine should return an reference to an array which contains the appropriate number of bind values, or return an undef if there is no more data to execute.

As a convenience, the ArrayTupleFetch attribute can also be used to specify a statement handle. In which case the fetchrow_arrayref() method will be called on the given statement handle in order to provide the bind values for each tuple execution.

The values specified via bind_param_array() or the @bind_values parameter may be either scalars, or arrayrefs. If any @bind_values are given, then execute_array will effectively call "bind_param_array" for each value before executing the statement. Values bound in this way are usually treated as SQL_VARCHAR types unless the driver can determine the correct type (which is rare), or unless bind_param, bind_param_inout, bind_param_array, or bind_param_inout_array has already been used to specify the type. See "bind_param_array" for details.

The ArrayTupleStatus attribute can be used to specify a reference to an array which will receive the execute status of each executed parameter tuple. Note the ArrayTupleStatus attribute was mandatory until DBI 1.38.

For tuples which are successfully executed, the element at the same ordinal position in the status array is the resulting rowcount. If the execution of a tuple causes an error, then the corresponding status array element will be set to a reference to an array containing the error code and error string set by the failed execution.

If any tuple execution returns an error, execute_array will return undef. In that case, the application should inspect the status array to determine which parameter tuples failed. Some databases may not continue executing tuples beyond the first failure. In this case the status array will either hold fewer elements, or the elements beyond the failure will be undef.

If all parameter tuples are successfully executed, execute_array returns the number tuples executed. If no tuples were executed, then execute_array() returns "0E0", just like execute() does, which Perl will treat as 0 but will regard as true.

For example:

$sth = $dbh->prepare("INSERT INTO staff (first_name, last_name) VALUES (?, ?)");
my $tuples = $sth->execute_array(
{ ArrayTupleStatus => \my @tuple_status },
\@first_names,
\@last_names,
);
if ($tuples) {
print "Successfully inserted $tuples records\n";
}
else {
for my $tuple (0..@last_names-1) {
my $status = $tuple_status[$tuple];
$status = [0, "Skipped"] unless defined $status;
next unless ref $status;
printf "Failed to insert (%s, %s): %s\n",
$first_names[$tuple], $last_names[$tuple], $status->[1];
}
}

Support for data returning statements such as SELECT is driver-specific and subject to change. At present, the default implementation provided by DBI only supports non-data returning statements.

Transaction semantics when using array binding are driver and database specific. If AutoCommit is on, the default DBI implementation will cause each parameter tuple to be individually committed (or rolled back in the event of an error). If AutoCommit is off, the application is responsible for explicitly committing the entire set of bound parameter tuples. Note that different drivers and databases may have different behaviours when some parameter tuples cause failures. In some cases, the driver or database may automatically rollback the effect of all prior parameter tuples that succeeded in the transaction; other drivers or databases may retain the effect of prior successfully executed parameter tuples. Be sure to check your driver and database for its specific behaviour.

Note that, in general, performance will usually be better with AutoCommit turned off, and using explicit commit after each execute_array call.

The execute_array method was added in DBI 1.22, and ArrayTupleFetch was added in 1.36.
Pеrl FAQ
perl -e 'print join"",map $$_[rand@$_],([0..9,'a'..'z','A'..'Z'])x30'
ИзображениеИзображение

Гость
проходил мимо

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение Гость » 2010-03-25 21:07:38

сделал так:

Код: Выделить всё

my ($month, $day, $time, $hostname, $servicename, $trash, $code, $message) = split /\s+/ , $line, 8;

        if ( $code eq 'reject:' )    {

        my %arr = split /[\[\]<>]/, $message; #Разбивка по любому из 4-х символов: [ ] < >
        my $j=0;
        my $mass=[];
        foreach my $i (keys %arr)
                          {
                         $mass[$j]=$i;
                         $mass[$j+1]=$arr{$i};
                         $j=$j+2;

  $dbh->do("INSERT INTO reject(`year`,`month`,`day`,`time`,`ip`,`mailfrom`,`rcptto`) VALUES ('$tim','$month','$day','$time','$mass[1]','$mass[5]','$mass[9]')");
Данные заводятся в нужные столбцы, только почему-то их до фига, то есть на один reject в таблицу заводится куча одинаковых значений, причем всегда одно количество -144. Как исключить совпадающие значения?

Аватара пользователя
ProFTP
подполковник
Сообщения: 3388
Зарегистрирован: 2008-04-13 1:50:04
Откуда: %&й
Контактная информация:

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение ProFTP » 2010-03-26 0:10:25

как это понимать @одинаковое"?

дайте вывод, тут вы по-моиму напутали, не красиво сделали...

Код: Выделить всё

...
        my $mass=[];
use Data::Dumper;
print Dumper \%arr;
exit;

        foreach my $i (keys %arr)
.....
какие ключи в хеше?


легче навеhное вот так написать:

Код: Выделить всё

  $dbh->do("INSERT INTO reject(`year`,`month`,`day`,`time`,`ip`,`mailfrom`,`rcptto`) VALUES ('$tim','$month','$day','$time','$arr{1}','$arr{2}','$arr{3}')");
Pеrl FAQ
perl -e 'print join"",map $$_[rand@$_],([0..9,'a'..'z','A'..'Z'])x30'
ИзображениеИзображение

Гость
проходил мимо

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение Гость » 2010-03-26 7:15:48

Код: Выделить всё

my $j=0;
        my $mass=[];
        use Data::Dumper;
        print Dumper \%arr;
        exit;
На выходе:

Код: Выделить всё

$VAR1 = {
          'RCPT from unknown' => '10.0.33.99',
          ' proto=ESMTP helo=' => 'utk.su',
          ': Helo command rejected: Host not found; from=' => 'root@utk.su',
          ': 450 4.7.1 ' => 'utk.su',
          ' to=' => 'root@utk.su',
          '
' => undef
        };

Аватара пользователя
ProFTP
подполковник
Сообщения: 3388
Зарегистрирован: 2008-04-13 1:50:04
Откуда: %&й
Контактная информация:

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение ProFTP » 2010-03-26 14:21:59

я бы сделал так, это красивее

Код: Выделить всё

        my %arr = split /[\[\]<>]/, $message; #Разбивка по любому из 4-х символов: [ ] < >

  my @a = reverse values %arr;

# reverse отсортировать чтобы ip ыбл первый элемент массива
# посомтреть в какой последовательности там элементы и
#
# use Data::Dumper;
#        print Dumper  \@a;

  $dbh->do("INSERT INTO reject(`year`,`month`,`day`,`time`,`ip`,`mailfrom`,`rcptto`) VALUES ('$tim','$month','$day','$time','$a[0]','$a[1]','$a[2]')");
...только если я правильно понял...
Pеrl FAQ
perl -e 'print join"",map $$_[rand@$_],([0..9,'a'..'z','A'..'Z'])x30'
ИзображениеИзображение

Гость
проходил мимо

Re: perl скрипт обработчика логов postfix

Непрочитанное сообщение Гость » 2010-03-30 14:52:37

отказался от перла и копаюсь с пхп. как-то он более мне понятен. пытюсь писать конфиг с нуля.

Код: Выделить всё

#!/usr/local/bin/php
<?php
$l = file ("/var/log/maillog");

#foreach - вывод всего содержимого массива $l
foreach ($l as $a)

        {
        #разбиваем строку по whitespace на 7 частей
        $arr = preg_split("[\s]", $a, 7);
#теперь есть массив $arr. Теперь надо бы как-то выделить письмо с определенным id и дальше парсить его
#тут ниже больше мысли. пока ничего не получается. Может подкинете мысль как сделать концептуально?
                if (preg_match ("/([a-z0-9]+)\:/i", $arr[5]))
                {
#               $arr[5] = $1;
                        if (preg_match ("/(client|size|from|to)=(\S+?)(\s|,)/", $arr[6]))
                        {
                        print "$arr[6]";
                        }
                }

#       print  "$arr[6]";
        }