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::BackgroundJob::ALL;
30 use SL::Helper::DateTime;
31 use SL::InstanceConfiguration;
36 use SL::System::Process;
37 use SL::System::TaskServer;
43 return if !$lx_office_conf{task_server}->{debug};
44 $::lxdebug->message(LXDebug::DEBUG1(), join(' ', "task server:", @_));
48 return SL::DB::Manager::AuthClient->get_all(where => [ '!task_server_user_id' => undef ]);
51 sub initialize_kivitendo {
58 $::lxdebug = LXDebug->new;
59 $::locale = Locale->new($::lx_office_conf{system}->{language});
61 $::auth = SL::Auth->new;
65 $::auth->set_client($client->id);
67 $::form->{__ERROR_HANDLER} = sub { die @_ };
69 $::instance_conf = SL::InstanceConfiguration->new;
70 $::request = SL::Request->new(
72 layout => SL::Layout::None->new,
75 die 'cannot reach auth db' unless $::auth->session_tables_present;
77 $::auth->restore_session;
78 $::auth->create_or_refresh_session;
80 my $login = $client->task_server_user->login;
82 die "cannot find user $login" unless %::myconfig = $::auth->read_user(login => $login);
83 die "cannot find locale for user $login" unless $::locale = Locale->new($::myconfig{countrycode} || $::lx_office_conf{system}->{language});
86 sub cleanup_kivitendo {
87 eval { SL::DB->client->dbh->rollback; };
89 $::auth->save_session;
90 $::auth->expire_sessions;
99 sub clean_before_sleeping {
100 SL::DBConnect::Cache->disconnect_all_and_clear;
101 SL::DB->db_cache->clear;
103 File::Temp::cleanup();
106 sub drop_privileges {
107 my $user = $lx_office_conf{task_server}->{run_as};
111 while (my @details = getpwent()) {
112 next unless $details[0] eq $user;
113 ($uid, $gid) = @details[2, 3];
119 print "Error: Cannot drop privileges to ${user}: user does not exist\n";
124 print "Error: Cannot drop group privileges to ${user} (group ID $gid): $!\n";
129 print "Error: Cannot drop user privileges to ${user} (user ID $uid): $!\n";
134 sub notify_on_failure {
137 my $cfg = $lx_office_conf{'task_server/notify_on_failure'} || {};
139 return if any { !$cfg->{$_} } qw(send_email_to email_from email_subject email_template);
143 return debug("Template " . $cfg->{email_template} . " missing!") unless -f $cfg->{email_template};
145 my $email_to = $cfg->{send_email_to};
146 if ($email_to !~ m{\@}) {
147 my %user = $::auth->read_user(login => $email_to);
148 return debug("cannot find user for notification $email_to") unless %user;
150 $email_to = $user{email};
151 return debug("user for notification " . $user{login} . " doesn't have a valid email address") unless $email_to =~ m{\@};
154 my $template = Template->new({
162 return debug("Could not create Template instance") unless $template;
164 $params{client} = $::auth->client;
168 $template->process($cfg->{email_template}, \%params, \$body);
171 from => $cfg->{email_from},
173 subject => $cfg->{email_subject},
174 content_type => 'text/plain',
181 debug("Sending a failure notification failed with an exception: $@");
188 # Initialize character type locale to be UTF-8 instead of C:
189 foreach my $locale (qw(de_DE.UTF-8 en_US.UTF-8)) {
190 last if setlocale('LC_CTYPE', $locale);
193 SL::LxOfficeConf->read($self->{configfile});
195 die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
197 if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
199 ERROR: The keys 'login' and/or 'client' are still present in the
200 section [task_server] in the configuration file. These keys are
201 deprecated. You have to configure the clients for which to run the
202 task server in the web admin interface.
204 The task server will refuse to start until the keys have been removed from
205 the configuration file.
210 initialize_kivitendo();
212 my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
213 if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
215 The authentication database requires an upgrade. Please login to
216 kivitendo's administration interface in order to apply it. The task
217 server cannot start until the upgrade has been applied.
227 sub run_once_for_all_clients {
228 initialize_kivitendo();
230 my $clients = enabled_clients();
232 foreach my $client (@{ $clients }) {
233 debug("Running for client ID " . $client->id . " (" . $client->name . ")");
236 initialize_kivitendo($client);
238 my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
241 debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
243 debug(" No jobs to execute found");
246 foreach my $job (@{ $jobs }) {
247 # Provide fresh global variables in case legacy code modifies
249 initialize_kivitendo($client);
251 my $history = $job->run;
253 debug(" Executed job " . $job->package_name .
254 "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
255 ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
257 notify_on_failure(history => $history) if $history && $history->has_failed;
264 my $error = $EVAL_ERROR;
265 debug("Exception during execution: ${error}");
266 notify_on_failure(exception => $error);
275 $SIG{'ALRM'} = 'IGNORE';
277 run_once_for_all_clients();
281 clean_before_sleeping();
283 my $seconds = 60 - (localtime)[0];
286 $SIG{'ALRM'} = 'IGNORE';
287 debug("Got woken up by SIGALRM");
290 sleep($seconds < 30 ? $seconds + 60 : $seconds);
293 die $@ unless $@ eq "Alarm!\n";
298 $exe_dir = SL::System::Process->exe_dir;
299 chdir($exe_dir) || die "Cannot change directory to ${exe_dir}\n";
301 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
303 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
305 die "No configuration file found." unless $file;
307 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
309 newdaemon(configfile => $file,
310 progname => 'kivitendo-background-jobs',
311 pidbase => SL::System::TaskServer::PID_BASE() . '/',