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;
30 use SL::Helper::DateTime;
31 use SL::InstanceConfiguration;
36 use SL::System::Process;
37 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 # Initialize character type locale to be UTF-8 instead of C:
190 foreach my $locale (qw(de_DE.UTF-8 en_US.UTF-8)) {
191 last if setlocale('LC_CTYPE', $locale);
194 SL::LxOfficeConf->read($self->{configfile});
196 die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
198 if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
200 ERROR: The keys 'login' and/or 'client' are still present in the
201 section [task_server] in the configuration file. These keys are
202 deprecated. You have to configure the clients for which to run the
203 task server in the web admin interface.
205 The task server will refuse to start until the keys have been removed from
206 the configuration file.
211 initialize_kivitendo();
213 my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
214 if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
216 The authentication database requires an upgrade. Please login to
217 kivitendo's administration interface in order to apply it. The task
218 server cannot start until the upgrade has been applied.
228 sub run_single_job_for_all_clients {
229 initialize_kivitendo();
231 my $clients = enabled_clients();
233 foreach my $client (@{ $clients }) {
234 debug("Running single job ID $run_single_job for client ID " . $client->id . " (" . $client->name . ")");
237 initialize_kivitendo($client);
239 my $job = SL::DB::Manager::BackgroundJob->find_by(id => $run_single_job);
242 debug(" Executing the following job: " . $job->package_name);
244 debug(" No jobs to execute found");
248 # Provide fresh global variables in case legacy code modifies
250 initialize_kivitendo($client);
252 my $history = $job->run;
254 debug(" Executed job " . $job->package_name .
255 "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
256 ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
258 notify_on_failure(history => $history) if $history && $history->has_failed;
264 my $error = $EVAL_ERROR;
265 $::lxdebug->message(LXDebug::WARN(), "Exception during execution: ${error}");
266 notify_on_failure(exception => $error);
273 sub run_once_for_all_clients {
274 initialize_kivitendo();
276 my $clients = enabled_clients();
278 foreach my $client (@{ $clients }) {
279 debug("Running for client ID " . $client->id . " (" . $client->name . ")");
282 initialize_kivitendo($client);
284 my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
287 debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
289 debug(" No jobs to execute found");
292 foreach my $job (@{ $jobs }) {
293 # Provide fresh global variables in case legacy code modifies
295 initialize_kivitendo($client);
297 my $history = $job->run;
299 debug(" Executed job " . $job->package_name .
300 "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
301 ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
303 notify_on_failure(history => $history) if $history && $history->has_failed;
310 my $error = $EVAL_ERROR;
311 $::lxdebug->message(LXDebug::WARN(), "Exception during execution: ${error}");
312 notify_on_failure(exception => $error);
320 if ($run_single_job) {
321 run_single_job_for_all_clients();
324 $::lxdebug->message(LXDebug::INFO(), "The task server for node " . SL::System::TaskServer::node_id() . " is up and running.");
327 $SIG{'ALRM'} = 'IGNORE';
329 run_once_for_all_clients();
333 clean_before_sleeping();
335 if (SL::System::Process::memory_usage_is_too_high()) {
336 debug("Memory usage too high - exiting.");
340 my $seconds = 60 - (localtime)[0];
343 $SIG{'ALRM'} = 'IGNORE';
344 debug("Got woken up by SIGALRM");
347 sleep($seconds < 30 ? $seconds + 60 : $seconds);
350 die $@ unless $@ eq "Alarm!\n";
357 '--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',
361 $exe_dir = SL::System::Process->exe_dir;
362 chdir($exe_dir) || die "Cannot change directory to ${exe_dir}\n";
364 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
366 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
368 die "No configuration file found." unless $file;
370 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
372 newdaemon(configfile => $file,
373 progname => 'kivitendo-background-jobs',
374 pidbase => SL::System::TaskServer::PID_BASE() . '/',
376 'run-job=i' => \$run_single_job,