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);
35 use SL::DB::AuthClient;
36 use SL::DB::BackgroundJob;
37 use SL::BackgroundJob::ALL;
39 use SL::Helper::DateTime;
40 use SL::InstanceConfiguration;
45 use SL::System::TaskServer;
51 return if !$lx_office_conf{task_server}->{debug};
52 $::lxdebug->message(LXDebug::DEBUG1(), join(' ', "task server:", @_));
56 return SL::DB::Manager::AuthClient->get_all(where => [ '!task_server_user_id' => undef ]);
59 sub initialize_kivitendo {
66 Form::disconnect_standard_dbh;
67 $::lxdebug = LXDebug->new;
68 $::locale = Locale->new($::lx_office_conf{system}->{language});
70 $::auth = SL::Auth->new;
74 $::auth->set_client($client->id);
76 $::form->{__ERROR_HANDLER} = sub { die @_ };
78 $::instance_conf = SL::InstanceConfiguration->new;
79 $::request = SL::Request->new(
81 layout => SL::Layout::None->new,
84 die 'cannot reach auth db' unless $::auth->session_tables_present;
86 $::auth->restore_session;
87 $::auth->create_or_refresh_session;
89 my $login = $client->task_server_user->login;
91 die "cannot find user $login" unless %::myconfig = $::auth->read_user(login => $login);
92 die "cannot find locale for user $login" unless $::locale = Locale->new($::myconfig{countrycode} || $::lx_office_conf{system}->{language});
95 sub cleanup_kivitendo {
96 eval { SL::DB::Auth->new->db->dbh->rollback; };
97 eval { SL::DB::BackgroundJob->new->db->dbh->rollback; };
99 $::auth->save_session;
100 $::auth->expire_sessions;
109 sub clean_before_sleeping {
110 Form::disconnect_standard_dbh;
111 SL::DBConnect::Cache->disconnect_all_and_clear;
112 SL::DB->db_cache->clear;
115 sub drop_privileges {
116 my $user = $lx_office_conf{task_server}->{run_as};
120 while (my @details = getpwent()) {
121 next unless $details[0] eq $user;
122 ($uid, $gid) = @details[2, 3];
128 print "Error: Cannot drop privileges to ${user}: user does not exist\n";
133 print "Error: Cannot drop group privileges to ${user} (group ID $gid): $!\n";
138 print "Error: Cannot drop user privileges to ${user} (user ID $uid): $!\n";
143 sub notify_on_failure {
146 my $cfg = $lx_office_conf{'task_server/notify_on_failure'} || {};
148 return if any { !$cfg->{$_} } qw(send_email_to email_from email_subject email_template);
152 return debug("Template " . $cfg->{email_template} . " missing!") unless -f $cfg->{email_template};
154 my $email_to = $cfg->{send_email_to};
155 if ($email_to !~ m{\@}) {
156 my %user = $::auth->read_user(login => $email_to);
157 return debug("cannot find user for notification $email_to") unless %user;
159 $email_to = $user{email};
160 return debug("user for notification " . $user{login} . " doesn't have a valid email address") unless $email_to =~ m{\@};
163 my $template = Template->new({
170 return debug("Could not create Template instance") unless $template;
172 $params{client} = $::auth->client;
176 $template->process($cfg->{email_template}, \%params, \$body);
179 from => $cfg->{email_from},
181 subject => $cfg->{email_subject},
182 content_type => 'text/plain',
184 message => Encode::decode('utf-8', $body),
189 debug("Sending a failure notification failed with an exception: $@");
196 SL::LxOfficeConf->read($self->{configfile});
198 die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
200 if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
202 ERROR: The keys 'login' and/or 'client' are still present in the
203 section [task_server] in the configuration file. These keys are
204 deprecated. You have to configure the clients for which to run the
205 task server in the web admin interface.
207 The task server will refuse to start until the keys have been removed from
208 the configuration file.
218 sub run_once_for_all_clients {
219 initialize_kivitendo();
221 my $clients = enabled_clients();
223 foreach my $client (@{ $clients }) {
224 debug("Running for client ID " . $client->id . " (" . $client->name . ")");
227 initialize_kivitendo($client);
229 my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
232 debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
234 debug(" No jobs to execute found");
237 foreach my $job (@{ $jobs }) {
238 # Provide fresh global variables in case legacy code modifies
240 initialize_kivitendo($client);
242 my $history = $job->run;
244 notify_on_failure(history => $history) if $history && $history->has_failed;
251 my $error = $EVAL_ERROR;
252 debug("Exception during execution: ${error}");
253 notify_on_failure(exception => $error);
262 run_once_for_all_clients();
266 clean_before_sleeping();
268 my $seconds = 60 - (localtime)[0];
270 local $SIG{'ALRM'} = sub {
271 debug("Got woken up by SIGALRM");
274 sleep($seconds < 30 ? $seconds + 60 : $seconds);
277 die $@ unless $@ eq "Alarm!\n";
283 $main::lxdebug->show_backtrace();
285 Job called ::end_of_request()!
287 This usually indicates success but should not be used by background jobs. A
288 backtrace has been logged. Please tell the job author to have a look at it.
295 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
297 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
299 die "No configuration file found." unless $file;
301 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
303 newdaemon(configfile => $file,
304 progname => 'kivitendo-background-jobs',
305 pidbase => SL::System::TaskServer::PID_BASE() . '/',