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.
12 push (@INC, $FindBin::Bin . '/../modules/fallback'); # Only use our own versions of modules if there's no system version.
15 use CGI qw( -no_xhtml);
20 use English qw(-no_match_vars);
22 use List::MoreUtils qw(any);
23 use List::Util qw(first);
24 use POSIX qw(setuid setgid);
27 use SL::DB::AuthClient;
28 use SL::DB::BackgroundJob;
29 use SL::BackgroundJob::ALL;
31 use SL::Helper::DateTime;
32 use SL::InstanceConfiguration;
37 use SL::System::Process;
38 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 SL::LxOfficeConf->read($self->{configfile});
191 die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
193 if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
195 ERROR: The keys 'login' and/or 'client' are still present in the
196 section [task_server] in the configuration file. These keys are
197 deprecated. You have to configure the clients for which to run the
198 task server in the web admin interface.
200 The task server will refuse to start until the keys have been removed from
201 the configuration file.
206 initialize_kivitendo();
208 my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
209 if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
211 The authentication database requires an upgrade. Please login to
212 kivitendo's administration interface in order to apply it. The task
213 server cannot start until the upgrade has been applied.
223 sub run_once_for_all_clients {
224 initialize_kivitendo();
226 my $clients = enabled_clients();
228 foreach my $client (@{ $clients }) {
229 debug("Running for client ID " . $client->id . " (" . $client->name . ")");
232 initialize_kivitendo($client);
234 my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
237 debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
239 debug(" No jobs to execute found");
242 foreach my $job (@{ $jobs }) {
243 # Provide fresh global variables in case legacy code modifies
245 initialize_kivitendo($client);
247 my $history = $job->run;
249 debug(" Executed job " . $job->package_name .
250 "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
251 ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
253 notify_on_failure(history => $history) if $history && $history->has_failed;
260 my $error = $EVAL_ERROR;
261 debug("Exception during execution: ${error}");
262 notify_on_failure(exception => $error);
271 $SIG{'ALRM'} = 'IGNORE';
273 run_once_for_all_clients();
277 clean_before_sleeping();
279 my $seconds = 60 - (localtime)[0];
282 $SIG{'ALRM'} = 'IGNORE';
283 debug("Got woken up by SIGALRM");
286 sleep($seconds < 30 ? $seconds + 60 : $seconds);
289 die $@ unless $@ eq "Alarm!\n";
294 $exe_dir = SL::System::Process->exe_dir;
295 chdir($exe_dir) || die "Cannot change directory to ${exe_dir}\n";
297 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
299 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
301 die "No configuration file found." unless $file;
303 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
305 newdaemon(configfile => $file,
306 progname => 'kivitendo-background-jobs',
307 pidbase => SL::System::TaskServer::PID_BASE() . '/',