From: Julien Lerouge Date: Fri, 22 Nov 2002 00:40:14 +0000 (+0000) Subject: Initial revision X-Git-Tag: r-0_0~1 X-Git-Url: http://git.polytechnique.org/?a=commitdiff_plain;h=cdf5b41f732a2887e402911069b85518a2265c9d;p=old-projects.git Initial revision --- cdf5b41f732a2887e402911069b85518a2265c9d diff --git a/muxdaemon/muxdaemon.conf b/muxdaemon/muxdaemon.conf new file mode 100644 index 0000000..e9d73e2 --- /dev/null +++ b/muxdaemon/muxdaemon.conf @@ -0,0 +1,65 @@ +#Cette commande doit renvoyer la charge +ldcmd = "cat /proc/loadavg | sed 's/^\([^ ]* \).*$/\1/'" + +#error_log +error_log = "/home/julien/toto" + +#Poll every 60 seconds +#Active poll : Time to wait between 2 launches in sec +active_poll = 0 + +#Time to wait when load is too high for any client in sec +sleep_poll = 60 + +#Nombre max de client à ne pas dépasser simultanément +max_client = 1 + + + #Charge au dessus de laquelle ce programme ne doit plus être lancé + max_load=0.3 + #Mbox + #Si mbox existe, des lots de nb_mails y sont lus et pipés dans command + #si mbox termine par un "/", c'est une boite maildir + #Pas encore implémenté + mbox=/var/mail/xorg-errors/ + #nombre de mails à traité ((1 command executée par mail) + nb_mails=50 + #Command to launch (datas are piped in it if mbox defined) + command="cat" + #Peut-on lancer plusieurs instances de ce prog simultanément ? yes or no + allow_many=no + #Priority relative aux autres de 1 à 100 + #Le client de priorité 100 est lancé 100 fois plus souvent que celui de + #priorité 1 (si possible) + priority=100 + #Lancer le prog en tant que : + run_as_user=julien + run_as_group=julien + #Temps minimum entre deux lancements d'un client en sec + min_schedule=0 + + + + + max_load=0.3 + command="/bin/sleep 1" + allow_many=no + priority=99 + + + + max_load=0.3 + command="/bin/sleep 3" + allow_many=no + priority=57 + run_as_user=jb + run_as_group=jb + + + + max_load=0.3 + command="/bin/sleep 2" + allow_many=no + priority=69 + + diff --git a/muxdaemon/muxdaemon.pl b/muxdaemon/muxdaemon.pl new file mode 100755 index 0000000..875aff0 --- /dev/null +++ b/muxdaemon/muxdaemon.pl @@ -0,0 +1,417 @@ +#!/usr/bin/perl + +use Config::General; +use Data::Dumper; +use Getopt::Std; +use IO::Handle; +use POSIX qw(setsid setuid setgid); +use POSIX ":sys_wait_h"; + + +################# +## Global vars ## +################# + +my $confile; +my %conf; +my %running; +my $reload_conf=0; +my $debug=0; +#For each process, uid and gid to set after fork +my %gid; +my %uid; + +#For sheduler +#Number of launch, for each daemon +my %launches; +my %ratio_launch_prio; +my %last_schedule; +my $priority_ref = ""; + +############### +## Functions ## +############### + +sub sigHUP_handler{ + &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)); +} + + +sub debug_msg(#){ + my $msg=shift; + if ($debug == 1){ + print STDERR $msg; + } +} + + +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'}){ + $debug = 1; + }else{ + $debug = 0; + } + +} + +sub init_launches(){ + my $min_priority=100; + foreach my $arg (keys %{$conf{'client'}}){ + $launches{"$arg"}=0; + $ratio_launch_prio{"$arg"}=0; + } +} + + +sub check_conf(){ + 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); + } + } +} + + +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"); +} + +sub get_load { + my $load = `$conf{"ldcmd"}`; + chop($load); + #&debug_msg("$load\n"); + return $load; +} + +#Return the list of client for which max_load is => ld +sub get_possible_client(#){ + my $ld=shift; + my @list=(); + #&debug_msg("get_possible_client, load = $ld\n"); + foreach my $arg (keys %{$conf{'client'}}){ + #&debug_msg("get_possible_client : Trying client $arg :"); + my $hash = ${$conf{'client'}}{$arg}; + if (${$hash}{'max_load'} >= $ld){ + #&debug_msg("max_load = ".${$hash}{'max_load'}." => in\n"); + push(@list,$arg); + }else{ + #&debug_msg("max_load = ".${$hash}{'max_load'}." => out\n"); + } + } + 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; +} + +#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; +} + + +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); + } + } +} + + +#Sends mail on the input of the command (pipe) +sub send_data(#){ + my $client=shift; + if (${${$conf{'client'}}{$client}}{'mbox'} =~ /\/$/){ + &debug_msg("Sending datas for ${${$conf{'client'}}{$client}}{'mbox'}\n"); + my @list=`ls -1 ${${$conf{'client'}}{$client}}{'mbox'}/new | head -n ${${$conf{'client'}}{$client}}{'nb_mails'}`; + if ($#list >= 0){ + #Send files, one by one + foreach my $fich (@list){ + &debug_msg("File : $fich\n"); + if (my $pid = open(CHILD, "|-")) { + CHILD->autoflush(1); + &debug_msg("Parent Pid $$\n"); + if (open(DATA,"<${${$conf{'client'}}{$client}}{'mbox'}/new/$fich")){ + while (my $line=){ + print CHILD "$line"; + #&debug_msg("Sending : $line"); + } + close(DATA); + }else{ + &debug_msg("Error opening ${${$conf{'client'}}{$client}}{'mbox'}/new/$fich : $!\n"); + exit(1); + } + close(CHILD); + #unlink("${${$conf{'client'}}{$client}}{'mbox'}/new/$fich"); + } else { + die "cannot fork: $!" unless defined $pid; + &change_uid_gid("$client"); + &debug_msg("Executing ${${$conf{'client'}}{$client}}{'command'} .... \n"); + exec("${${$conf{'client'}}{$client}}{'command'}"); + } + } + }else{ + &debug_msg("No mail available\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'}); +} + + +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_launches; + &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'}); + } + } +} + + + +########## +## Main ## +########## + +$SIG{HUP}=\&sigHUP_handler; +&init(); +&init_launches(); +&check_conf(); +&daemonize(); +&main_loop(); + +