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(setlocale 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 # Initialize character type locale to be UTF-8 instead of C:
190 foreach my $locale (qw(de_DE.UTF-8 en_US.UTF-8)) {
191 last if setlocale('LC_CTYPE', $locale);
194 SL::LxOfficeConf->read($self->{configfile});
196 die "Missing section [task_server] in config file" unless $lx_office_conf{task_server};
198 if ($lx_office_conf{task_server}->{login} || $lx_office_conf{task_server}->{client}) {
200 ERROR: The keys 'login' and/or 'client' are still present in the
201 section [task_server] in the configuration file. These keys are
202 deprecated. You have to configure the clients for which to run the
203 task server in the web admin interface.
205 The task server will refuse to start until the keys have been removed from
206 the configuration file.
211 initialize_kivitendo();
213 my $dbupdater_auth = SL::DBUpgrade2->new(form => $::form, auth => 1)->parse_dbupdate_controls;
214 if ($dbupdater_auth->unapplied_upgrade_scripts($::auth->dbconnect)) {
216 The authentication database requires an upgrade. Please login to
217 kivitendo's administration interface in order to apply it. The task
218 server cannot start until the upgrade has been applied.
228 sub run_once_for_all_clients {
229 initialize_kivitendo();
231 my $clients = enabled_clients();
233 foreach my $client (@{ $clients }) {
234 debug("Running for client ID " . $client->id . " (" . $client->name . ")");
237 initialize_kivitendo($client);
239 my $jobs = SL::DB::Manager::BackgroundJob->get_all_need_to_run;
242 debug(" Executing the following jobs: " . join(' ', map { $_->package_name } @{ $jobs }));
244 debug(" No jobs to execute found");
247 foreach my $job (@{ $jobs }) {
248 # Provide fresh global variables in case legacy code modifies
250 initialize_kivitendo($client);
252 my $history = $job->run;
254 debug(" Executed job " . $job->package_name .
255 "; result: " . (!$history ? "no return value" : $history->has_failed ? "failed" : "succeeded") .
256 ($history && $history->has_failed ? "; error: " . $history->error_col : ""));
258 notify_on_failure(history => $history) if $history && $history->has_failed;
265 my $error = $EVAL_ERROR;
266 debug("Exception during execution: ${error}");
267 notify_on_failure(exception => $error);
276 $SIG{'ALRM'} = 'IGNORE';
278 run_once_for_all_clients();
282 clean_before_sleeping();
284 my $seconds = 60 - (localtime)[0];
287 $SIG{'ALRM'} = 'IGNORE';
288 debug("Got woken up by SIGALRM");
291 sleep($seconds < 30 ? $seconds + 60 : $seconds);
294 die $@ unless $@ eq "Alarm!\n";
299 $exe_dir = SL::System::Process->exe_dir;
300 chdir($exe_dir) || die "Cannot change directory to ${exe_dir}\n";
302 mkdir SL::System::TaskServer::PID_BASE() if !-d SL::System::TaskServer::PID_BASE();
304 my $file = first { -f } ("${exe_dir}/config/kivitendo.conf", "${exe_dir}/config/lx_office.conf", "${exe_dir}/config/kivitendo.conf.default");
306 die "No configuration file found." unless $file;
308 $file = File::Spec->abs2rel(Cwd::abs_path($file), Cwd::abs_path($exe_dir));
310 newdaemon(configfile => $file,
311 progname => 'kivitendo-background-jobs',
312 pidbase => SL::System::TaskServer::PID_BASE() . '/',