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.
 
  14 use CGI qw( -no_xhtml);
 
  19 use English qw(-no_match_vars);
 
  21 use List::MoreUtils qw(any);
 
  22 use List::Util qw(first);
 
  23 use POSIX qw(setlocale setuid setgid);
 
  26 use SL::DB::AuthClient;
 
  27 use SL::DB::BackgroundJob;
 
  28 use SL::System::Process;
 
  29 use SL::BackgroundJob::ALL;
 
  31 use SL::Helper::DateTime;
 
  32 use SL::InstanceConfiguration;
 
  37 use SL::System::Process;
 
  38 use SL::System::TaskServer;
 
  45   return if !$lx_office_conf{task_server}->{debug};
 
  46   $::lxdebug->message(LXDebug::DEBUG1(), join(' ', "task server:", @_));
 
  50   return SL::DB::Manager::AuthClient->get_all(where => [ '!task_server_user_id' => undef ]);
 
  53 sub initialize_kivitendo {
 
  60   $::lxdebug       = LXDebug->new;
 
  61   $::locale        = Locale->new($::lx_office_conf{system}->{language});
 
  63   $::auth          = SL::Auth->new;
 
  67   $::auth->set_client($client->id);
 
  69   $::form->{__ERROR_HANDLER} = sub { die @_ };
 
  71   $::instance_conf = SL::InstanceConfiguration->new;
 
  72   $::request       = SL::Request->new(
 
  74     layout         => SL::Layout::None->new,
 
  77   die 'cannot reach auth db'               unless $::auth->session_tables_present;
 
  79   $::auth->restore_session;
 
  80   $::auth->create_or_refresh_session;
 
  82   my $login = $client->task_server_user->login;
 
  84   die "cannot find user $login"            unless %::myconfig = $::auth->read_user(login => $login);
 
  85   die "cannot find locale for user $login" unless $::locale   = Locale->new($::myconfig{countrycode} || $::lx_office_conf{system}->{language});
 
  88 sub cleanup_kivitendo {
 
  89   eval { SL::DB->client->dbh->rollback; };
 
  91   $::auth->save_session;
 
  92   $::auth->expire_sessions;
 
 101 sub clean_before_sleeping {
 
 102   SL::DBConnect::Cache->disconnect_all_and_clear;
 
 103   SL::DB->db_cache->clear;
 
 105   File::Temp::cleanup();
 
 108 sub drop_privileges {
 
 109   my $user = $lx_office_conf{task_server}->{run_as};
 
 113   while (my @details = getpwent()) {
 
 114     next unless $details[0] eq $user;
 
 115     ($uid, $gid) = @details[2, 3];
 
 121     print "Error: Cannot drop privileges to ${user}: user does not exist\n";
 
 126     print "Error: Cannot drop group privileges to ${user} (group ID $gid): $!\n";
 
 131     print "Error: Cannot drop user privileges to ${user} (user ID $uid): $!\n";
 
 136 sub notify_on_failure {
 
 139   my $cfg = $lx_office_conf{'task_server/notify_on_failure'} || {};
 
 141   return if any { !$cfg->{$_} } qw(send_email_to email_from email_subject email_template);
 
 145   return debug("Template " . $cfg->{email_template} . " missing!") unless -f $cfg->{email_template};
 
 147   my $email_to = $cfg->{send_email_to};
 
 148   if ($email_to !~ m{\@}) {
 
 149     my %user = $::auth->read_user(login => $email_to);
 
 150     return debug("cannot find user for notification $email_to") unless %user;
 
 152     $email_to = $user{email};
 
 153     return debug("user for notification " . $user{login} . " doesn't have a valid email address") unless $email_to =~ m{\@};
 
 156   my $template  = Template->new({
 
 164   return debug("Could not create Template instance") unless $template;
 
 166   $params{client} = $::auth->client;
 
 170     $template->process($cfg->{email_template}, \%params, \$body);
 
 173       from         => $cfg->{email_from},
 
 175       subject      => $cfg->{email_subject},
 
 176       content_type => 'text/plain',
 
 183     debug("Sending a failure notification failed with an exception: $@");
 
 190   # Initialize character type locale to be UTF-8 instead of C:
 
 191   foreach my $locale (qw(de_DE.UTF-8 en_US.UTF-8)) {
 
 192     last if setlocale('LC_CTYPE', $locale);
 
 195   SL::LxOfficeConf->read($self->{configfile});
 
 197   die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
 
 199   if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
 
 201 ERROR: The keys 'login' and/or 'client' are still present in the
 
 202 section [task_server] in the configuration file. These keys are
 
 203 deprecated. You have to configure the clients for which to run the
 
 204 task server in the web admin interface.
 
 206 The task server will refuse to start until the keys have been removed from
 
 207 the configuration file.
 
 212   initialize_kivitendo();
 
 214   my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
 
 215   if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
 
 217 The authentication database requires an upgrade. Please login to
 
 218 kivitendo's administration interface in order to apply it. The task
 
 219 server cannot start until the upgrade has been applied.
 
 229 sub run_single_job_for_all_clients {
 
 230   initialize_kivitendo();
 
 232   my $clients = enabled_clients();
 
 234   foreach my $client (@{ $clients }) {
 
 235     debug("Running single job ID $run_single_job for client ID " . $client->id . " (" . $client->name . ")");
 
 238       initialize_kivitendo($client);
 
 240       my $job = SL::DB::Manager::BackgroundJob->find_by(id => $run_single_job);
 
 243         debug(" Executing the following job: " . $job->package_name);
 
 245         debug(" No jobs to execute found");
 
 249       # Provide fresh global variables in case legacy code modifies
 
 251       initialize_kivitendo($client);
 
 253       my $history = $job->run;
 
 255       debug("   Executed job " . $job->package_name .
 
 256             "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
 
 257             ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
 
 259       notify_on_failure(history => $history) if $history && $history->has_failed;
 
 265       my $error = $EVAL_ERROR;
 
 266       debug("Exception during execution: ${error}");
 
 267       notify_on_failure(exception => $error);
 
 274 sub run_once_for_all_clients {
 
 275   initialize_kivitendo();
 
 277   my $clients = enabled_clients();
 
 279   foreach my $client (@{ $clients }) {
 
 280     debug("Running for client ID " . $client->id . " (" . $client->name . ")");
 
 283       initialize_kivitendo($client);
 
 285       my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
 
 288         debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
 
 290         debug(" No jobs to execute found");
 
 293       foreach my $job (@{ $jobs }) {
 
 294         # Provide fresh global variables in case legacy code modifies
 
 296         initialize_kivitendo($client);
 
 298         my $history = $job->run;
 
 300         debug("   Executed job " . $job->package_name .
 
 301               "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
 
 302               ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
 
 304         notify_on_failure(history => $history) if $history && $history->has_failed;
 
 311       my $error = $EVAL_ERROR;
 
 312       debug("Exception during execution: ${error}");
 
 313       notify_on_failure(exception => $error);
 
 321   if ($run_single_job) {
 
 322     run_single_job_for_all_clients();
 
 327     $SIG{'ALRM'} = 'IGNORE';
 
 329     run_once_for_all_clients();
 
 333     clean_before_sleeping();
 
 335     if (SL::System::Process::memory_usage_is_too_high()) {
 
 339     my $seconds = 60 - (localtime)[0];
 
 342         $SIG{'ALRM'} = 'IGNORE';
 
 343         debug("Got woken up by SIGALRM");
 
 346       sleep($seconds < 30 ? $seconds + 60 : $seconds);
 
 349       die $@ unless $@ eq "Alarm!\n";
 
 356     '--run-job=<id>' => 'Run the single job with the database ID <id> no matter if it is active or when its next execution is supposed to be; the daemon will exit afterwards',
 
 360 $exe_dir = SL::System::Process->exe_dir;
 
 361 chdir($exe_dir) || die "Cannot change directory to ${exe_dir}\n";
 
 363 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
 
 365 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
 
 367 die "No configuration file found." unless $file;
 
 369 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
 
 371 newdaemon(configfile => $file,
 
 372           progname   => 'kivitendo-background-jobs',
 
 373           pidbase    => SL::System::TaskServer::PID_BASE() . '/',
 
 375             'run-job=i' => \$run_single_job,