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 $::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->client->dbh->rollback; };
98 $::auth->save_session;
99 $::auth->expire_sessions;
108 sub clean_before_sleeping {
109 SL::DBConnect::Cache->disconnect_all_and_clear;
110 SL::DB->db_cache->clear;
112 File::Temp::cleanup();
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.
213 initialize_kivitendo();
215 my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
216 if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
218 The authentication database requires an upgrade. Please login to
219 kivitendo's administration interface in order to apply it. The task
220 server cannot start until the upgrade has been applied.
230 sub run_once_for_all_clients {
231 initialize_kivitendo();
233 my $clients = enabled_clients();
235 foreach my $client (@{ $clients }) {
236 debug("Running for client ID " . $client->id . " (" . $client->name . ")");
239 initialize_kivitendo($client);
241 my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
244 debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
246 debug(" No jobs to execute found");
249 foreach my $job (@{ $jobs }) {
250 # Provide fresh global variables in case legacy code modifies
252 initialize_kivitendo($client);
254 my $history = $job->run;
256 notify_on_failure(history => $history) if $history && $history->has_failed;
263 my $error = $EVAL_ERROR;
264 debug("Exception during execution: ${error}");
265 notify_on_failure(exception => $error);
274 $SIG{'ALRM'} = 'IGNORE';
276 run_once_for_all_clients();
280 clean_before_sleeping();
282 my $seconds = 60 - (localtime)[0];
285 $SIG{'ALRM'} = 'IGNORE';
286 debug("Got woken up by SIGALRM");
289 sleep($seconds < 30 ? $seconds + 60 : $seconds);
292 die $@ unless $@ eq "Alarm!\n";
299 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
301 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
303 die "No configuration file found." unless $file;
305 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
307 newdaemon(configfile => $file,
308 progname => 'kivitendo-background-jobs',
309 pidbase => SL::System::TaskServer::PID_BASE() . '/',