10   unshift(@INC, $FindBin::Bin . '/../modules/override'); # Use our own versions of various modules (e.g. YAML).
 
  11   push   (@INC, $FindBin::Bin . '/..');                  # '.' will be removed from @INC soon.
 
  12   push   (@INC, $FindBin::Bin . '/../modules/fallback'); # Only use our own versions of modules if there's no system version.
 
  15 use CGI qw( -no_xhtml);
 
  20 use English qw(-no_match_vars);
 
  22 use List::MoreUtils qw(any);
 
  23 use List::Util qw(first);
 
  24 use POSIX qw(setuid setgid);
 
  27 use SL::DB::AuthClient;
 
  28 use SL::DB::BackgroundJob;
 
  29 use SL::BackgroundJob::ALL;
 
  31 use SL::Helper::DateTime;
 
  32 use SL::InstanceConfiguration;
 
  37 use SL::System::Process;
 
  38 use SL::System::TaskServer;
 
  44   return if !$lx_office_conf{task_server}->{debug};
 
  45   $::lxdebug->message(LXDebug::DEBUG1(), join(' ', "task server:", @_));
 
  49   return SL::DB::Manager::AuthClient->get_all(where => [ '!task_server_user_id' => undef ]);
 
  52 sub initialize_kivitendo {
 
  59   $::lxdebug       = LXDebug->new;
 
  60   $::locale        = Locale->new($::lx_office_conf{system}->{language});
 
  62   $::auth          = SL::Auth->new;
 
  66   $::auth->set_client($client->id);
 
  68   $::form->{__ERROR_HANDLER} = sub { die @_ };
 
  70   $::instance_conf = SL::InstanceConfiguration->new;
 
  71   $::request       = SL::Request->new(
 
  73     layout         => SL::Layout::None->new,
 
  76   die 'cannot reach auth db'               unless $::auth->session_tables_present;
 
  78   $::auth->restore_session;
 
  79   $::auth->create_or_refresh_session;
 
  81   my $login = $client->task_server_user->login;
 
  83   die "cannot find user $login"            unless %::myconfig = $::auth->read_user(login => $login);
 
  84   die "cannot find locale for user $login" unless $::locale   = Locale->new($::myconfig{countrycode} || $::lx_office_conf{system}->{language});
 
  87 sub cleanup_kivitendo {
 
  88   eval { SL::DB->client->dbh->rollback; };
 
  90   $::auth->save_session;
 
  91   $::auth->expire_sessions;
 
 100 sub clean_before_sleeping {
 
 101   SL::DBConnect::Cache->disconnect_all_and_clear;
 
 102   SL::DB->db_cache->clear;
 
 104   File::Temp::cleanup();
 
 107 sub drop_privileges {
 
 108   my $user = $lx_office_conf{task_server}->{run_as};
 
 112   while (my @details = getpwent()) {
 
 113     next unless $details[0] eq $user;
 
 114     ($uid, $gid) = @details[2, 3];
 
 120     print "Error: Cannot drop privileges to ${user}: user does not exist\n";
 
 125     print "Error: Cannot drop group privileges to ${user} (group ID $gid): $!\n";
 
 130     print "Error: Cannot drop user privileges to ${user} (user ID $uid): $!\n";
 
 135 sub notify_on_failure {
 
 138   my $cfg = $lx_office_conf{'task_server/notify_on_failure'} || {};
 
 140   return if any { !$cfg->{$_} } qw(send_email_to email_from email_subject email_template);
 
 144   return debug("Template " . $cfg->{email_template} . " missing!") unless -f $cfg->{email_template};
 
 146   my $email_to = $cfg->{send_email_to};
 
 147   if ($email_to !~ m{\@}) {
 
 148     my %user = $::auth->read_user(login => $email_to);
 
 149     return debug("cannot find user for notification $email_to") unless %user;
 
 151     $email_to = $user{email};
 
 152     return debug("user for notification " . $user{login} . " doesn't have a valid email address") unless $email_to =~ m{\@};
 
 155   my $template  = Template->new({
 
 163   return debug("Could not create Template instance") unless $template;
 
 165   $params{client} = $::auth->client;
 
 169     $template->process($cfg->{email_template}, \%params, \$body);
 
 172       from         => $cfg->{email_from},
 
 174       subject      => $cfg->{email_subject},
 
 175       content_type => 'text/plain',
 
 182     debug("Sending a failure notification failed with an exception: $@");
 
 189   SL::LxOfficeConf->read($self->{configfile});
 
 191   die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
 
 193   if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
 
 195 ERROR: The keys 'login' and/or 'client' are still present in the
 
 196 section [task_server] in the configuration file. These keys are
 
 197 deprecated. You have to configure the clients for which to run the
 
 198 task server in the web admin interface.
 
 200 The task server will refuse to start until the keys have been removed from
 
 201 the configuration file.
 
 206   initialize_kivitendo();
 
 208   my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
 
 209   if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
 
 211 The authentication database requires an upgrade. Please login to
 
 212 kivitendo's administration interface in order to apply it. The task
 
 213 server cannot start until the upgrade has been applied.
 
 223 sub run_once_for_all_clients {
 
 224   initialize_kivitendo();
 
 226   my $clients = enabled_clients();
 
 228   foreach my $client (@{ $clients }) {
 
 229     debug("Running for client ID " . $client->id . " (" . $client->name . ")");
 
 232       initialize_kivitendo($client);
 
 234       my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
 
 237         debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
 
 239         debug(" No jobs to execute found");
 
 242       foreach my $job (@{ $jobs }) {
 
 243         # Provide fresh global variables in case legacy code modifies
 
 245         initialize_kivitendo($client);
 
 247         my $history = $job->run;
 
 249         debug("   Executed job " . $job->package_name .
 
 250               "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
 
 251               ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
 
 253         notify_on_failure(history => $history) if $history && $history->has_failed;
 
 260       my $error = $EVAL_ERROR;
 
 261       debug("Exception during execution: ${error}");
 
 262       notify_on_failure(exception => $error);
 
 271     $SIG{'ALRM'} = 'IGNORE';
 
 273     run_once_for_all_clients();
 
 277     clean_before_sleeping();
 
 279     my $seconds = 60 - (localtime)[0];
 
 282         $SIG{'ALRM'} = 'IGNORE';
 
 283         debug("Got woken up by SIGALRM");
 
 286       sleep($seconds < 30 ? $seconds + 60 : $seconds);
 
 289       die $@ unless $@ eq "Alarm!\n";
 
 294 $exe_dir = SL::System::Process->exe_dir;
 
 295 chdir($exe_dir) || die "Cannot change directory to ${exe_dir}\n";
 
 297 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
 
 299 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
 
 301 die "No configuration file found." unless $file;
 
 303 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
 
 305 newdaemon(configfile => $file,
 
 306           progname   => 'kivitendo-background-jobs',
 
 307           pidbase    => SL::System::TaskServer::PID_BASE() . '/',