a37cd03d1a7a9f223c26d3521d09fc203e1c1213
[kivitendo-erp.git] / SL / System / TaskServer.pm
1 package SL::System::TaskServer;
2
3 use strict;
4
5 use parent qw(Rose::Object);
6
7 use Rose::Object::MakeMethods::Generic (
8   scalar => [ qw(last_command_output) ],
9 );
10
11 use File::Slurp;
12 use File::Spec::Functions qw(:ALL);
13 use File::Temp;
14 use Sys::Hostname ();
15
16 use SL::System::Process;
17
18 use constant {
19   OK           =>  0,
20   ERR_PID_FILE => -1,
21   ERR_PROCESS  => -2,
22 };
23
24 use constant PID_BASE => "users/pid";
25
26 my $node_id;
27
28 sub status {
29   my ($self) = @_;
30
31   my $pid = $self->_read_pid;
32   return ERR_PID_FILE unless $pid;
33
34   return kill(0, $pid) ? OK : ERR_PROCESS;
35 }
36
37 sub is_running {
38   my ($self) = @_;
39
40   return $self->status == OK;
41 }
42
43 sub start {
44   my ($self) = @_;
45
46   return $self->_run_script_command('start');
47 }
48
49 sub start_if_not_running {
50   my ($self) = @_;
51
52   $self->start unless $self->is_running;
53 }
54
55 sub stop {
56   my ($self) = @_;
57
58   return $self->_run_script_command('stop');
59 }
60
61 sub wake_up {
62   my ($self) = @_;
63
64   my $pid = $self->_read_pid;
65   return undef unless $pid;
66   return kill('ALRM', $pid) ? 1 : undef;
67 }
68
69 sub node_id {
70   return $node_id if $node_id;
71
72   $node_id = ($::lx_office_conf{task_server} // {})->{node_id} || Sys::Hostname::hostname();
73
74   return $node_id;
75 }
76
77 #
78 # private methods
79 #
80
81 sub _read_pid {
82   my ($self) = @_;
83
84   my $exe_dir = SL::System::Process->exe_dir;
85
86   foreach my $conf (qw(kivitendo.conf lx_office.conf kivitendo.conf.default)) {
87     my $pid_file_path = catfile(catdir($exe_dir, splitdir(PID_BASE())), "config.${conf}.pid");
88
89     return join('', read_file($pid_file_path)) * 1 if -f $pid_file_path;
90   }
91 }
92
93 sub _run_script_command {
94   my ($self, $command) = @_;
95
96   my $exe              = catfile(catdir(SL::System::Process->exe_dir, 'scripts'), 'task_server.pl');
97   my $temp_file        = File::Temp->new;
98   my $file_name        = $temp_file->filename;
99
100   $temp_file->close;
101
102   system "${exe} ${command} >> ${file_name} 2>&1";
103
104   $self->last_command_output(read_file($file_name));
105
106   return $? == 0 ? 1 : undef;
107 }
108
109 1;
110 __END__
111
112 =pod
113
114 =encoding utf8
115
116 =head1 NAME
117
118 SL::System::TaskServer - programmatic interface to the external task server component
119
120 =head1 SYNOPSIS
121
122   # Create interface
123   my $task_server = SL->TaskServer->new;
124
125   # Start the server if it is not running
126   if (!$task_server->is_running) {
127     $task_server->start;
128   }
129
130   # Stop it if it is running
131   if ($task_server->is_running) {
132     $task_server->stop;
133   }
134
135 =head1 FUNCTIONS
136
137 =over 4
138
139 =item C<is_running>
140
141 Returns C<trueish> if the server is running. This is done by using
142 Perl's C<kill> function with a "signal" of C<0> for the process ID
143 which in turn is read from the daemon's PID file.
144
145 If the PID file is not found or if C<kill> returns a non-successful
146 value then a C<falsish> value is returned.
147
148 =item C<last_command_output>
149
150 Returns the output of the last C<system> command executed, e.g. from a
151 call to L<start> or L<stop>.
152
153 =item C<start>
154
155 Starts the task server. Does not check whether or not it is running,
156 neither before not after trying to start it.
157
158 Returns C<1> if the system command C<./scripts/task_server.pl start>
159 exits with an exit code of C<0> and C<undef> otherwise.
160
161 The command's output can be queried with L<last_command_output>.
162
163 =item C<status>
164
165 Queries the task server status. Returns one of these values:
166
167 =over 4
168
169 =item *
170
171 C<OK> or C<0>: the task server is running and signals can be sent to
172 it.
173
174 =item *
175
176 C<ERR_PID_FILE> or C<-1>: the PID file could not be found or read
177
178 =item *
179
180 C<ERR_PROCESS> or C<-2>: the PID file could was found and read, but
181 it's not possible to send signals to the process, e.g. because it is
182 not running or owned by a different user ID.
183
184 =back
185
186 =item C<stop>
187
188 Stops the task server. Does not check whether or not it is running,
189 neither before not after trying to start it.
190
191 Returns C<1> if the system command C<./scripts/task_server.pl stop>
192 exits with an exit code of C<0> and C<undef> otherwise.
193
194 The command's output can be queried with L<last_command_output>.
195
196 =item C<wake_up>
197
198 Sends a signal to the task server process causing it to wake up and
199 process its job queue immediately.
200
201 Returns C<1> if the signal could be sent and C<undef> otherwise.
202
203 =back
204
205 =head1 BUGS
206
207 Nothing here yet.
208
209 =head1 AUTHOR
210
211 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
212
213 =cut