From 6e805a57656176983615c96a325d00d71cb1af85 Mon Sep 17 00:00:00 2001 From: Loic Le Loarer Date: Wed, 16 Jul 2003 09:41:39 +0000 Subject: [PATCH 1/1] =?utf8?q?passage=20=E0=20'use=20strict'=20et=20=E0=20?= =?utf8?q?-w,=20indispensables=20=E0=20tout=20programme=20perl=20=20=20=20?= =?utf8?q?=20\!\!\!,=20correction=20des=20probl=E8mes=20de=20r=E9f=E9rence?= =?utf8?q?s=20sur=20les=20tables=20de=20hash,=20=20=20=20=20suppression=20?= =?utf8?q?des=20appels=20inutiles=20=E0=20/bin/sh=20en=20lisant=20directem?= =?utf8?q?ent=20le=20load=20=20=20=20=20et=20en=20utilisant=20opendir/read?= =?utf8?q?dir=20pour=20lire=20les=20r=E9pertoires,=20ajout=20de=20la=20=20?= =?utf8?q?=20=20=20possibilit=E9=20de=20mettre=20les=20fichiers=20=E0=20tr?= =?utf8?q?aiter=20sur=20la=20ligne=20de=20commande=20=20=20=20=20du=20prog?= =?utf8?q?ramme=20client?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- muxdaemon/muxdaemon.pl | 697 +++++++++++++++++++++++++++---------------------- 1 file changed, 379 insertions(+), 318 deletions(-) diff --git a/muxdaemon/muxdaemon.pl b/muxdaemon/muxdaemon.pl index 6d5c4cc..c59f674 100755 --- a/muxdaemon/muxdaemon.pl +++ b/muxdaemon/muxdaemon.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/perl -w use Config::General; use Data::Dumper; @@ -7,12 +7,14 @@ use IO::Handle; use POSIX qw(setsid setuid setgid); use POSIX ":sys_wait_h"; +use strict; + ################# -## Global vars ## +## Global vars ## ################# -my $confile; +my $confile = ''; my %conf; my %running; my $reload_conf=0; @@ -32,378 +34,440 @@ my %last_schedule; ############### sub sigHUP_handler{ - &debug_msg("got SIGHUP\n"); - $reload_conf=1; + &debug_msg("got SIGHUP\n"); + $reload_conf=1; } - + #Read Configuration and init global vars sub read_conf { - my $file = shift; - $config = new Config::General( -file =>"$file", - -AllowMultiOptions =>"yes", - -LowerCaseNames =>"yes",); - %conf = $config->getall; - &debug_msg(Dumper(\%conf)); + my $file = shift; + my $config = new Config::General( -file =>"$file", + -AllowMultiOptions =>"yes", + -LowerCaseNames =>"yes",); + %conf = $config->getall; + &debug_msg(Dumper(\%conf)); } sub debug_msg(#){ - my $text=shift; - print STDERR $text unless ($debug == 0); + my $text=shift; + print STDERR $text unless ($debug == 0); } - + sub init { - #getopts('hf:', \%opts) or die "Illegal program option. ($0 -h for list)\n"; - #&debug_msg(Dumper(\%opts)); - - if ($confile eq ""){ - my $path = `pwd`; - chomp($path); - if ($path){ - $confile="$path/muxdaemon.conf"; - }else{ - print STDERR "Error : unable to get working directory, $!\n"; - exit(1); - } - } - - if ( not getopts('dhf:', \%opts) or $opts{'h'}) { - print STDERR "\nThis is mail processing Daemon :\n", - "-h - this help message\n", - "-f - use as config file\n", - "-d - debug mode\n"; - exit(1); - } - - if ($opts{'f'}){ - if (-r "$opts{'f'}") { - print STDERR "Using $opts{'f'} as config file\n"; - &read_conf("$opts{'f'}"); - } else { - print STDERR "$opts{'f'} : not a valid config file\n"; - exit(1); - } - } else { - print STDERR "Using $confile as config file\n"; - if (-r "$confile") { - &read_conf("$confile"); - } else { - print STDERR "No valid configuration file found, aborting\n"; - exit(1); - } - } - - if ($opts{'d'}){ - print STDERR "Running in debug mode ...\n"; - $debug = 1; - }else{ - $debug = 0; - } + + if ($confile eq ""){ + my $path = `pwd`; + chomp($path); + if ($path){ + $confile="$path/muxdaemon.conf"; + }else{ + print STDERR "Error : unable to get working directory, $!\n"; + exit(1); + } + } + + my %opts; + if ( not getopts('dhf:', \%opts) or $opts{'h'}) { + print STDERR "\nThis is mail processing Daemon :\n", + "-h - this help message\n", + "-f - use as config file\n", + "-d - debug mode\n"; + exit(1); + } + + if ($opts{'f'}){ + if (-r "$opts{'f'}") { + print STDERR "Using $opts{'f'} as config file\n"; + &read_conf("$opts{'f'}"); + } else { + print STDERR "$opts{'f'} : not a valid config file\n"; + exit(1); + } + } else { + print STDERR "Using $confile as config file\n"; + if (-r "$confile") { + &read_conf("$confile"); + } else { + print STDERR "No valid configuration file found, aborting\n"; + exit(1); + } + } + + if ($opts{'d'}){ + print STDERR "Running in debug mode ...\n"; + $debug = 1; + }else{ + $debug = 0; + } } #init useful variables for scheduler sub init_sched(){ - foreach my $arg (keys %{$conf{'client'}}){ - $launches{"$arg"}=0; - $ratio_launch_prio{"$arg"}=0; - $last_schedule{"$arg"}=0; - } + foreach my $arg (keys %{$conf{'client'}}){ + $launches{$arg} = 0; + $ratio_launch_prio{$arg} = 0; + $last_schedule{$arg} = 0; + } } -#check configuration file +#check configuration file sub check_conf{ - &debug_msg("Salut\n"); - print STDERR "Checking configuration file for wrong user/groups, schedules, ....\n"; - foreach my $arg (keys %{$conf{'client'}}){ - #La conf du client courant - my $hash=${$conf{'client'}}{"$arg"}; - if (${$hash}{'run_as_user'}){ - my $id = getpwnam("${$hash}{'run_as_user'}"); - if ($id){ - $uid{"$arg"} = $id; - }else{ - print STDERR " Error, user ${$hash}{'run_as_user'} does not exist, please check...\n"; - exit(1); - } - } - if (${$hash}{'run_as_group'}){ - my $id = getgrnam("${$hash}{'run_as_group'}"); - if ($id){ - $gid{"$arg"} = $id; - }else{ - print STDERR " Error, group ${$hash}{'run_as_group'} does not exist, please check...\n"; - exit(1); - } - } - if (${$hash}{'allow_many'} eq "yes" and ${$hash}{'min_schedule'}){ - print STDERR " Error, allow_many and min_schedule defined for $arg\n"; - exit(1); - } - } + &debug_msg("Salut\n"); + print STDERR "Checking configuration file for wrong user/groups, schedules, ....\n"; + foreach my $arg (keys %{$conf{'client'}}){ + #La conf du client courant + my %hash = %{$conf{'client'}{$arg}}; + if ($hash{'run_as_user'}) { + my $id = getpwnam($hash{'run_as_user'}); + if ($id){ + $conf{'client'}{$arg}{'uid'} = $id; + } else { + print STDERR " Error, user $hash{'run_as_user'} does not exist, please check...\n"; + exit(1); + } + } + if ($hash{'run_as_group'}){ + my $id = getgrnam("$hash{'run_as_group'}"); + if ($id){ + $conf{'client'}{$arg}{'gid'} = $id; + } else { + print STDERR " Error, group $hash{'run_as_group'} does not exist, please check...\n"; + exit(1); + } + } + if ($hash{'allow_many'} eq "yes" and $hash{'min_schedule'}){ + print STDERR " Error, allow_many and min_schedule defined for $arg\n"; + exit(1); + } + } } #make the program run in daemon mode sub daemonize { - print STDERR "Forking ... \n"; - chdir '/' or die "Can't chdir to /: $!"; - open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; - open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!"; - if ($conf{'error_log'}){ - open STDERR, ">>$conf{'error_log'}" or die "Can't write to $conf{'error_log'}: $!"; - }else{ - open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!"; - } - defined(my $pid = fork) or die "Can't fork: $!"; - exit if $pid; - setsid or die "Can't start a new session: $!"; - #umask 0; - &debug_msg("Daemon started\n"); + print STDERR "Forking ... \n"; + chdir '/' or die "Can't chdir to /: $!"; + open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; + open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!"; + if ($conf{'error_log'}){ + open STDERR, ">>$conf{'error_log'}" or die "Can't write to $conf{'error_log'}: $!"; + }else{ + open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!"; + } + defined(my $pid = fork) or die "Can't fork: $!"; + exit if $pid; + setsid or die "Can't start a new session: $!"; + #umask 0; + &debug_msg("Daemon started\n"); } #returns current load or any good value sub get_load { - my $load = `$conf{"ldcmd"}`; - chomp($load); - return $load; + my $load = 100; + if (open(LOAD, '; + ($load) = ($line =~ m/(\d+\.\d+)/); + close(LOAD); + } + return $load; } #Return the list of client for which max_load is => ld #param : load sub get_possible_client(#){ - my $ld=shift; - my @list=(); - - foreach my $arg (keys %{$conf{'client'}}){ - my $hash = ${$conf{'client'}}{$arg}; - if (${$hash}{'max_load'} >= $ld){ - push(@list,$arg); - } - } - return \@list; + my $ld = shift; + my @list = (); + + foreach my $arg (keys %{$conf{'client'}}){ + my %hash = %{$conf{'client'}{$arg}}; + if ($hash{'max_load'} >= $ld){ + push(@list,$arg); + } + } + return \@list; } - + #Return the client that should be executed according to priorities #given in conf file # Refaire avec calcul du min de launches/prio ! sub get_next(#){ - my $ref=shift; - my @possible_client = @{$ref}; - #List des proc executes assez souvent - my $client=""; - my $min_ratio=-1; - - foreach my $arg (@possible_client){ - if ($ratio_launch_prio{"$arg"} < $min_ratio or $min_ratio == -1){ - $min_ratio=$ratio_launch_prio{"$arg"}; - $client=$arg; - } - } - - if ($client){ - #Update ratio for him - $launches{"$client"}++; - $ratio_launch_prio{"$client"}=$launches{"$client"}/${${$conf{'client'}}{$client}}{'priority'}; - return $client; - }else{ - &debug_msg("No client runnable in get_next\n"); - return; - } - return; + my $ref = shift; + my @possible_client = @{$ref}; + #List des proc executes assez souvent + my $client = ''; + my $min_ratio = -1; + + foreach my $arg (@possible_client) { + if ($ratio_launch_prio{$arg} < $min_ratio or $min_ratio == -1){ + $min_ratio=$ratio_launch_prio{$arg}; + $client=$arg; + } + } + + if ($client) { + #Update ratio for him + $launches{$client}++; + $ratio_launch_prio{$client}=$launches{$client}/$conf{'client'}{$client}{'priority'}; + return $client; + }else{ + &debug_msg("No client runnable in get_next\n"); + return; + } + return; } #Elimine les clients présent dans la liste qui sont en cours d'éxécution et #qui n'ont pas le allow_many (pour pas les éxécuter deux fois) #Elimine aussi les clients qui ont terminé depuis moins que min_schedule sub trim_possible_client(#){ - my $ref=shift; - my @out_list; - - - foreach my $arg (@$ref){ - if (${${$conf{'client'}}{$arg}}{'allow_many'} eq "no"){ - #Exec only one - if ((time - $last_schedule{$arg}) >= ${${$conf{'client'}}{$arg}}{'min_schedule'}){ - push(@out_list,$arg) unless grep($_ eq $arg, values %running); - } - }else{ - push(@out_list,$arg); - } - } - - return \@out_list; + my $ref=shift; + my @out_list; + + + foreach my $arg (@$ref){ + if (${${$conf{'client'}}{$arg}}{'allow_many'} eq "no"){ + #Exec only one + if ((time - $last_schedule{$arg}) >= ${${$conf{'client'}}{$arg}}{'min_schedule'}){ + push(@out_list,$arg) unless grep($_ eq $arg, values %running); + } + }else{ + push(@out_list,$arg); + } + } + + return \@out_list; } sub change_uid_gid(#){ - my $client=shift; - if ($gid{"$client"}){ - &debug_msg("Changing gid to $gid{$client} for $client ..."); - if (POSIX::setgid($gid{"$client"})){ - &debug_msg("done\n"); - }else{ - &debug_msg("error, $!\n"); - exit(1); - } - } - if ($uid{"$client"}){ - &debug_msg("Changing uid to $uid{$client} for $client ..."); - if (POSIX::setuid($uid{"$client"})){ - &debug_msg("done\n"); - }else{ - &debug_msg("error, $!\n"); - exit(1); - } - } + my $client = shift; + if ($conf{'client'}{$client}{'gid'}){ + &debug_msg("Changing gid to $conf{'client'}{$client}{'gid'} for $client ..."); +# $( = $conf{'client'}{$client}{'gid'}; +# $) = $conf{'client'}{$client}{'gid'}; + if (POSIX::setgid($conf{'client'}{$client}{'gid'})){ + &debug_msg("done\n"); + } else { + &debug_msg("error, $!\n"); + exit(1); + } + } + if ($conf{'client'}{$client}{'uid'}){ + &debug_msg("Changing uid to $conf{'client'}{$client}{'uid'} for $client ..."); +# $< = $conf{'client'}{$client}{'uid'}; +# $> = $conf{'client'}{$client}{'uid'}; + if (POSIX::setuid($conf{'client'}{$client}{'uid'})){ + &debug_msg("done\n"); + } else { + &debug_msg("error, $!\n"); + exit(1); + } + } } #Sends mail on the input of the command (pipe) -sub send_data(#){ - my $client = shift; - my $hash = ${$conf{'client'}}{$client}; - if (${$hash}{'mbox'} =~ /\/$/){ #ok, maildir format - my $path = "${$hash}{'mbox'}"; - $path =~ s/\/$//igo; - &debug_msg("Sending datas for ${$hash}{'mbox'}\n"); - my @list=`ls -1 $path/new | head -n ${$hash}{'nb_mails'}`; - if ($#list >= 0){ - #Send files, one by one - foreach my $fich (@list){ - chomp($fich); - &debug_msg("File : $fich\n"); - if (my $pid = open(CHILD, "|-")) { - CHILD->autoflush(1); - if (open(DATA,"<$path/new/$fich")){ - while (my $line=){ - print CHILD "$line"; - } - close(DATA); - &debug_msg("$path/new/$fich -> $path/cur/$fich ... "); - if (rename("$path/new/$fich","$path/cur/$fich")){ - &debug_msg("ok\n"); - }else{ - print STDERR "Error moving $path/new/$fich, keeped : $!\n"; - } - }else{ - print STDERR "Error opening $path/new/$fich : $!\n"; - exit(1); - } - close(CHILD); - } else { - die "cannot fork: $!" unless defined $pid; - &change_uid_gid("$client"); - &debug_msg("Executing ${$hash}{'command'} .... \n"); - exec("${$hash}{'command'}"); - } - } - }else{ - &debug_msg("No mail available\n"); - } - }else{ - &debug_msg("Mbox format not yet supported.\n"); - exit(1); - } +sub send_data(#) { + # TODO check that the child has properly finished before moving + # the file tu cur/ + my $client = shift; + my $fich = shift; + &debug_msg("run $conf{'client'}{$client}{'command'} with file $fich for $client\n"); + my $pid = fork; + if (!defined $pid) { + die "cannot fork $!"; + } elsif ($pid == 0) { + # fils + if (open(STDIN, '<', $fich)) { + &change_uid_gid($client); + &debug_msg("sending output to $conf{'client'}{$client}{'command'} with file $fich for $client $fich openned\n"); + exec $conf{'client'}{$client}{'command'} or die "exec error $!"; + } else { + die "Error opening $fich : $!\n"; + } + } else { + # père + wait; + &debug_msg("sending output to $conf{'client'}{$client}{'command'} with file $fich for $client done\n"); + # return true if the program exited normaly + return ($? >> 8) == 0; + } +} + +sub list_files(#) { + my $client = shift; + my %hash = %{$conf{'client'}{$client}}; + if ($hash{'mbox'} =~ /\/$/) { #ok, maildir format + my @tomove; + my @args; + my $path = $hash{'mbox'}; + $path =~ s/\/$//igo; # suppress the final / + &debug_msg("Sending datas for $hash{'mbox'}\n"); + # read the dir + if (opendir MAILDIR, "$path/new") { + my $nb_left = $hash{'nb_mails'}; + while (my $fich = readdir MAILDIR) { + # skip . and .. + next if ($fich eq "." or $fich eq ".."); + # read the dir until the max number of mail is done + &debug_msg("nb_left : $nb_left\n"); + last if ($nb_left-- == 0); + &debug_msg("File : $fich\n"); + # if add_to_cmdline is true, add to the list + if ($hash{'add_to_cmdline'}) { + push @args, $fich; + } else { + # else run the command for each file + if (&send_data($client, "$path/new/$fich")) { + push @tomove, $fich; + } + } + &debug_msg("Done file : $fich, $?\n"); + } + closedir MAILDIR; + } else { + print STDERR "Error opening $path/new : $!\n"; + } + # if add_to_cmdline is true and there are files to treat, + # run the command + if ($hash{'add_to_cmdline'} and scalar @args > 0) { + chdir "$path/new" or die "Cannot chdir to $path/new : $!"; + &debug_msg("run $hash{'command'} with files @args for $client\n"); + my @cmd = split(/ /, $hash{'command'}); + push @cmd, @args; + &debug_msg("@cmd\n"); + my $pid = fork; + if (!defined $pid) { + die "cannot fork $!"; + } elsif ($pid == 0) { + # fils + &change_uid_gid($client); + exec @cmd or die "exec error $!"; + } else { + # père + wait; + my $status = $? >> 8; + if ($status == 0) { + # if the command didn't fail, mark the files to be moved + @tomove = @args; + } + } + } + # if there is files to move, moves them + foreach my $fich (@tomove) { + &debug_msg("$path/new/$fich -> $path/cur/$fich ... "); + if (rename("$path/new/$fich", "$path/cur/$fich")){ + &debug_msg("ok\n"); + } else { + print STDERR "Error moving $path/new/$fich, keeped : $!\n"; + } + } + } else { + &debug_msg("Mbox format not yet supported.\n"); + exit(1); + } } - - + + sub launch(#){ - my $client=shift; - #Launch the command of a simple client - &debug_msg("Son $client, launching ${${$conf{'client'}}{$client}}{'command'}\n"); - #Change uid and gid if needed - &change_uid_gid("$client"); - exec(${${$conf{'client'}}{$client}}{'command'}); + my $client = shift; + &change_uid_gid($client); + #Launch the command of a simple client + &debug_msg("Son $client, launching $conf{'client'}{$client}{'command'}\n"); + exec($conf{'client'}{$client}{'command'}); } sub main_loop { - - my $ld; - my $nchild; - - while (1) { - - if ($reload_conf == 1){ - #We have to load conf again - $reload_conf=0; - &debug_msg("Reloading configuration .... \n"); - &debug_msg(" Waiting all child to terminate ..."); - while ((my $kid=wait) != -1){ - $nchild--; - delete($running{$kid}); - } - &debug_msg("done\n"); - &debug_msg(" Restarting ..."); - &init; - &init_sched; - &debug_msg("done\n"); - } - - - if ($nchild >= $conf{'max_client'}){ - #We have to wait for one child (Blocking)" - &debug_msg("Waiting for childs to terminate\n"); - my $kid=wait; - if ($kid>0){ - &debug_msg("Kid=$kid est mort, c'était un $running{$kid}\n"); - $nchild--; - delete($running{$kid}); - } - }else{ - #Rip child terminated - &debug_msg("Checking dead childs....\n"); - while ( (my $kid = waitpid(-1, WNOHANG)) > 0 ){ - &debug_msg("Kid=$kid est mort, c'était un $running{$kid}\n"); - $nchild--; - delete($running{$kid}); - } - - #Look for a client to execute - my $ld = &get_load; - &debug_msg("Load = $ld, processing\n"); - my $possible_client=&get_possible_client($ld); - my @list=@{$possible_client}; - &debug_msg("Avant Trim : \n"); - &debug_msg(Dumper($possible_client)); - $possible_client=&trim_possible_client($possible_client); - &debug_msg("Après Trim : \n"); - &debug_msg(Dumper($possible_client)); - my $client=&get_next($possible_client); - if ($client){ - #sleep(1); - &debug_msg("$client was chosen, launching\n"); - $last_schedule{$client} = time ; - if (my $pid = fork){ - #Pere, on marque qu'on l'a lancé - $running{$pid}="$client"; - $nchild++; - &debug_msg("$client launched as PID $pid\n"); - &debug_msg(Dumper(\%running)); - &debug_msg(Dumper(\%launches)); - }else{ - #Fils - if (${${$conf{'client'}}{$client}}{'mbox'}){ - #We have to send data ourselves - &send_data("$client"); - }else{ - #We only have to execute a command - &launch("$client"); - } - exit(0); - } - }else{ - &debug_msg("No client runable\n"); - sleep($conf{'sleep_poll'}); - } - sleep($conf{'active_poll'}); - } - } + + my $ld; + my $nchild = 0; + + while (1) { + + if ($reload_conf == 1){ + #We have to load conf again + $reload_conf=0; + &debug_msg("Reloading configuration .... \n"); + &debug_msg(" Waiting all child to terminate ..."); + while ((my $kid=wait) != -1){ + $nchild--; + delete($running{$kid}); + } + &debug_msg("done\n"); + &debug_msg(" Restarting ..."); + &init; + &init_sched; + &debug_msg("done\n"); + } + + + if ($nchild >= $conf{'max_client'}){ + #We have to wait for one child (Blocking)" + &debug_msg("Waiting for childs to terminate\n"); + my $kid=wait; + if ($kid>0){ + &debug_msg("Kid=$kid est mort, c'était un $running{$kid}\n"); + $nchild--; + delete($running{$kid}); + } + } else { + #Rip child terminated + &debug_msg("Checking dead childs....\n"); + while ( (my $kid = waitpid(-1, WNOHANG)) > 0 ){ + &debug_msg("Kid=$kid est mort, c'était un $running{$kid}\n"); + $nchild--; + delete($running{$kid}); + } + + #Look for a client to execute + my $ld = &get_load; + &debug_msg("Load = $ld, processing\n"); + my $possible_client = &get_possible_client($ld); + my @list = @{$possible_client}; + &debug_msg("Avant Trim : \n"); + &debug_msg(Dumper($possible_client)); + $possible_client = &trim_possible_client($possible_client); + &debug_msg("Après Trim : \n"); + &debug_msg(Dumper($possible_client)); + my $client = &get_next($possible_client); + if ($client) { + &debug_msg("$client was chosen, launching\n"); + $last_schedule{$client} = time ; + my $pid = fork; + if (!defined $pid) { + print STDERR "cannot fork: $!\n"; + } elsif ($pid != 0) { + #Pere, on marque qu'on l'a lancé + $running{$pid} = $client; + $nchild++; + &debug_msg("$client launched as PID $pid\n"); + &debug_msg(Dumper(\%running)); + &debug_msg(Dumper(\%launches)); + } else { + #Fils + if ($conf{'client'}{$client}{'mbox'}) { + #We have to send data ourselves + &list_files($client); + } else { + #We only have to execute a command + &launch($client); + } + exit(0); + } + } else { + &debug_msg("No client runable\n"); + sleep($conf{'sleep_poll'}); + } + sleep($conf{'active_poll'}); + } + } } @@ -416,7 +480,4 @@ $SIG{HUP}=\&sigHUP_handler; &init(); &check_conf(); &init_sched(); -&daemonize(); &main_loop(); - - -- 2.1.4