4 use List::MoreUtils qw(any);
12 use lib "$FindBin::Bin/..";
14 use SL::System::Process;
15 $exe_dir = SL::System::Process::exe_dir;
17 unshift @INC, "${exe_dir}/modules/override"; # Use our own versions of various modules (e.g. YAML).
18 push @INC, "${exe_dir}/modules/fallback"; # Only use our own versions of modules if there's no system version.
19 unshift @INC, $exe_dir;
21 chdir($exe_dir) || die "Cannot change directory to ${exe_dir}\n";
24 use CGI qw( -no_xhtml);
30 use English qw(-no_match_vars);
32 use List::Util qw(first);
33 use POSIX qw(setuid setgid);
36 use SL::DB::AuthClient;
37 use SL::DB::BackgroundJob;
38 use SL::BackgroundJob::ALL;
40 use SL::Helper::DateTime;
41 use SL::InstanceConfiguration;
46 use SL::System::TaskServer;
52 return if !$lx_office_conf{task_server}->{debug};
53 $::lxdebug->message(LXDebug::DEBUG1(), join(' ', "task server:", @_));
57 return SL::DB::Manager::AuthClient->get_all(where => [ '!task_server_user_id' => undef ]);
60 sub initialize_kivitendo {
67 Form::disconnect_standard_dbh;
68 $::lxdebug = LXDebug->new;
69 $::locale = Locale->new($::lx_office_conf{system}->{language});
71 $::auth = SL::Auth->new;
75 $::auth->set_client($client->id);
77 $::form->{__ERROR_HANDLER} = sub { die @_ };
79 $::instance_conf = SL::InstanceConfiguration->new;
80 $::request = SL::Request->new(
82 layout => SL::Layout::None->new,
85 die 'cannot reach auth db' unless $::auth->session_tables_present;
87 $::auth->restore_session;
88 $::auth->create_or_refresh_session;
90 my $login = $client->task_server_user->login;
92 die "cannot find user $login" unless %::myconfig = $::auth->read_user(login => $login);
93 die "cannot find locale for user $login" unless $::locale = Locale->new($::myconfig{countrycode} || $::lx_office_conf{system}->{language});
96 sub cleanup_kivitendo {
97 eval { SL::DB::Auth->new->db->dbh->rollback; };
98 eval { SL::DB::BackgroundJob->new->db->dbh->rollback; };
100 $::auth->save_session;
101 $::auth->expire_sessions;
110 sub clean_before_sleeping {
111 Form::disconnect_standard_dbh;
112 SL::DBConnect::Cache->disconnect_all_and_clear;
113 SL::DB->db_cache->clear;
116 sub drop_privileges {
117 my $user = $lx_office_conf{task_server}->{run_as};
121 while (my @details = getpwent()) {
122 next unless $details[0] eq $user;
123 ($uid, $gid) = @details[2, 3];
129 print "Error: Cannot drop privileges to ${user}: user does not exist\n";
134 print "Error: Cannot drop group privileges to ${user} (group ID $gid): $!\n";
139 print "Error: Cannot drop user privileges to ${user} (user ID $uid): $!\n";
144 sub notify_on_failure {
147 my $cfg = $lx_office_conf{'task_server/notify_on_failure'} || {};
149 return if any { !$cfg->{$_} } qw(send_email_to email_from email_subject email_template);
153 return debug("Template " . $cfg->{email_template} . " missing!") unless -f $cfg->{email_template};
155 my $email_to = $cfg->{send_email_to};
156 if ($email_to !~ m{\@}) {
157 my %user = $::auth->read_user(login => $email_to);
158 return debug("cannot find user for notification $email_to") unless %user;
160 $email_to = $user{email};
161 return debug("user for notification " . $user{login} . " doesn't have a valid email address") unless $email_to =~ m{\@};
164 my $template = Template->new({
171 return debug("Could not create Template instance") unless $template;
173 $params{client} = $::auth->client;
177 $template->process($cfg->{email_template}, \%params, \$body);
180 from => $cfg->{email_from},
182 subject => $cfg->{email_subject},
183 content_type => 'text/plain',
185 message => Encode::decode('utf-8', $body),
190 debug("Sending a failure notification failed with an exception: $@");
197 SL::LxOfficeConf->read($self->{configfile});
199 die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
201 if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
203 ERROR: The keys 'login' and/or 'client' are still present in the
204 section [task_server] in the configuration file. These keys are
205 deprecated. You have to configure the clients for which to run the
206 task server in the web admin interface.
208 The task server will refuse to start until the keys have been removed from
209 the configuration file.
214 initialize_kivitendo();
216 my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
217 if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
219 The authentication database requires an upgrade. Please login to
220 kivitendo's administration interface in order to apply it. The task
221 server cannot start until the upgrade has been applied.
231 sub run_once_for_all_clients {
232 initialize_kivitendo();
234 my $clients = enabled_clients();
236 foreach my $client (@{ $clients }) {
237 debug("Running for client ID " . $client->id . " (" . $client->name . ")");
240 initialize_kivitendo($client);
242 my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
245 debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
247 debug(" No jobs to execute found");
250 foreach my $job (@{ $jobs }) {
251 # Provide fresh global variables in case legacy code modifies
253 initialize_kivitendo($client);
255 my $history = $job->run;
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 run_once_for_all_clients();
279 clean_before_sleeping();
281 my $seconds = 60 - (localtime)[0];
283 local $SIG{'ALRM'} = sub {
284 debug("Got woken up by SIGALRM");
287 sleep($seconds < 30 ? $seconds + 60 : $seconds);
290 die $@ unless $@ eq "Alarm!\n";
296 $main::lxdebug->show_backtrace();
298 Job called ::end_of_request()!
300 This usually indicates success but should not be used by background jobs. A
301 backtrace has been logged. Please tell the job author to have a look at it.
308 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
310 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
312 die "No configuration file found." unless $file;
314 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
316 newdaemon(configfile => $file,
317 progname => 'kivitendo-background-jobs',
318 pidbase => SL::System::TaskServer::PID_BASE() . '/',