]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/offline/offline.pl
getting closer..
[Evergreen.git] / Open-ILS / src / offline / offline.pl
1 #!/usr/bin/perl
2 use strict; use warnings;
3 use DBI;
4 use CGI;
5 use OpenSRF::System;
6 use OpenSRF::Utils::Logger qw/$logger/;
7 use OpenILS::Application::AppUtils;
8 use OpenILS::Event;
9 use OpenSRF::EX qw/:try/;
10 use JSON;
11 use Data::Dumper;
12 use OpenILS::Utils::Fieldmapper;
13 use Digest::MD5 qw/md5_hex/;
14 use OpenSRF::Utils qw/:daemon/;
15 use OpenILS::Utils::OfflineStore;
16 $DBI::trace = 1;
17
18 my $U = "OpenILS::Application::AppUtils";
19 my $DB = "OpenILS::Utils::OfflineStore";
20 my $SES = "${DB}::Session";
21 my $SCRIPT = "OpenILS::Utils::OfflineStore::Script";
22
23 our %config;
24
25 # --------------------------------------------------------------------
26 # Load the config
27 # --------------------------------------------------------------------
28 #do '##CONFIG##/upload-server.pl';
29 do 'offline-config.pl';
30
31
32 my $cgi                 = new CGI;
33 my $basedir             = $config{base_dir};
34 my $wsname              = $cgi->param('ws');
35 my $org                 = $cgi->param('org');
36 my $authtoken   = $cgi->param('ses') || "";
37 my $seskey              = $cgi->param('seskey');
38 my $action              = $cgi->param('action'); # - create, load, execute, status
39 my $requestor; 
40 my $wsobj;
41 my $orgobj;
42 my $evt;
43
44
45 &ol_init;
46 &ol_runtime_init;
47 &ol_do_action;
48
49
50 # --------------------------------------------------------------------
51 # Set it all up
52 # This function should behave as a child_init might behave in case 
53 # this is moved to mod_perl
54 # --------------------------------------------------------------------
55 sub ol_init {
56
57         my $s = "";
58         $s .= "$_=" . $cgi->param($_) . "\n" for $cgi->param;
59         warn '-'x60 ."\n$s\n" . '-'x60 ."\n";
60
61         $DB->DBFile($config{db});
62         OpenSRF::System->bootstrap_client(config_file => $config{bootstrap} ); 
63 }
64
65
66 # --------------------------------------------------------------------
67 # Finds the requestor and other info specific to this request
68 # --------------------------------------------------------------------
69 sub ol_runtime_init {
70
71         # fetch the requestor object
72         ($requestor, $evt) = $U->checkses($authtoken);
73         ol_handle_result($evt) if $evt;
74
75
76         # try the param, the workstation, and finally the user's ws org
77         if(!$org) { 
78                 $wsobj = ol_fetch_workstation($wsname);
79                 $org = $wsobj->owning_lib if $wsobj;
80                 $org = $requestor->ws_ou unless $org;
81                 ol_handle_result(OpenILS::Event->new('OFFLINE_NO_ORG')) unless $org;
82         }
83 }
84
85
86 # --------------------------------------------------------------------
87 # Runs the requested action
88 # --------------------------------------------------------------------
89 sub ol_do_action {
90
91         my $payload;
92
93         if( $action eq 'create' ) {
94                 
95                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_UPLOAD');
96                 ol_handle_result($evt) if $evt;
97                 $payload = ol_create_session();
98
99         } elsif( $action eq 'load' ) {
100
101                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_UPLOAD');
102                 ol_handle_result($evt) if $evt;
103                 $payload = ol_load();
104
105         } elsif( $action eq 'execute' ) {
106
107                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_EXECUTE');
108                 ol_handle_result($evt) if $evt;
109                 $payload = ol_execute();
110
111         } elsif( $action eq 'status' ) {
112
113                 $evt = $U->check_perms($requestor->id, $org, 'OFFLINE_VIEW');
114                 ol_handle_result($evt) if $evt;
115                 $payload = ol_status();
116         }
117
118         ol_handle_event('SUCCESS', payload => $payload );
119 }
120
121
122 # --------------------------------------------------------------------
123 # Creates a new session
124 # --------------------------------------------------------------------
125 sub ol_create_session {
126
127         my $desc = $cgi->param('desc') || "";
128         $seskey ||=  time . "_${$}_" . int(rand() * 100);
129
130         $logger->debug("offline: user ".$requestor->id.
131                 " creating new session with key $seskey and description $desc");
132
133         $SES->create(
134                 {       
135                         key                     => $seskey,
136                         org                     => $org,
137                         description => $desc,
138                         creator         => $requestor->id,
139                         create_time => CORE::time(), 
140                 } 
141         );
142
143         return $seskey;
144 }
145
146
147 # --------------------------------------------------------------------
148 # Holds the meta-info for a script file
149 # --------------------------------------------------------------------
150 sub ol_create_script {
151         my $count = shift;
152
153         my $session = ol_find_session($seskey);
154         my $delta = $cgi->param('delta') || 0;
155
156         my $script = $session->add_to_scripts( 
157                 {
158                         requestor       => $requestor->id,
159                         create_time     => CORE::time,
160                         workstation     => $wsname,
161                         logfile         => "$basedir/pending/$org/$seskey/$wsname.log",
162                         time_delta      => $delta,
163                         count                   => $count,
164                 }
165         );
166 }
167
168 # --------------------------------------------------------------------
169 # Finds the current session in the db
170 # --------------------------------------------------------------------
171 sub ol_find_session {
172         my $ses = $SES->retrieve($seskey);
173         ol_handle_event('OFFLINE_INVALID_SESSION', payload => $seskey) unless $ses;
174         return $ses;
175 }
176
177 # --------------------------------------------------------------------
178 # Finds a script object in the DB based on workstation and seskey
179 # --------------------------------------------------------------------
180 sub ol_find_script {
181         my $ws = shift || $wsname;
182         my $sk = shift || $seskey;
183         my ($script) = $SCRIPT->search( session => $seskey, workstation => $ws );
184         return $script;
185 }
186
187 # --------------------------------------------------------------------
188 # Creates a new script in the database and loads the new script file
189 # --------------------------------------------------------------------
190 sub ol_load {
191         my $session = ol_find_session;
192         my $handle      = $cgi->upload('file');
193         my $outdir = "$basedir/pending/$org/$seskey";
194         my $outfile = "$outdir/$wsname.log";
195
196         ol_handl_event('OFFLINE_SESSION_FILE_EXISTS') if ol_find_script();
197         ol_handle_event('OFFLINE_SESSION_ACTIVE') if $session->in_process;
198
199         qx/mkdir -p $outdir/;
200         my $x = 0;
201         open(FILE, ">>$outfile") or ol_handle_event('OFFLINE_FILE_ERROR');
202         while( <$handle> ) { print FILE; $x++;}
203         close(FILE);
204
205         ol_create_script($x);
206
207         return undef;
208 }
209
210
211 # --------------------------------------------------------------------
212 sub ol_handle_result {
213         my $obj = shift;
214         my $json = JSON->perl2JSON($obj);
215
216         if( $cgi->param('html')) {
217                 my $html = "<html><body onload='xulG.handle_event($json)></body></html>";
218                 print "content-type: text/html\n\n";
219                 print "$html\n";
220
221         } else {
222
223                 print "content-type: text/plain\n\n";
224                 print "$json\n";
225         }
226
227         exit(0);
228 }
229
230 # --------------------------------------------------------------------
231 sub ol_handle_event {
232         my( $evt, @args ) = @_;
233         ol_handle_result(OpenILS::Event->new($evt, @args));
234 }
235
236
237 # --------------------------------------------------------------------
238 sub ol_flesh_session {
239         my $session = shift;
240         my %data;
241
242         map { $data{$_} = $session->$_ } $session->columns;
243         $data{scripts} = [];
244
245         for my $script ($session->scripts) {
246                 my %sdata;
247                 map { $sdata{$_} = $script->$_ } $script->columns;
248
249                 # the client doesn't need this info
250                 delete $sdata{session};
251                 delete $sdata{id};
252                 delete $sdata{logfile};
253
254                 push( @{$data{scripts}}, \%sdata );
255         }
256
257         return \%data;
258 }
259
260
261 # --------------------------------------------------------------------
262 # Returns various information on the sessions and scripts
263 # --------------------------------------------------------------------
264 sub ol_status {
265
266         my $type = $cgi->param('status_type') || "scripts";
267
268         if( $type eq 'scripts' ) {
269                 my $session = ol_find_session();
270                 ol_handle_result(ol_flesh_session($session));
271
272         } elsif( $type eq 'sessions' ) {
273                 my @sessions = $SES->search( org => $org );
274
275                 # can I do this in the DB without raw SQL?
276                 @sessions = sort { $a->create_time <=> $b->create_time } @sessions; 
277                 my @data;
278                 push( @data, ol_flesh_session($_) ) for @sessions;
279                 ol_handle_result(\@data);
280         }
281 }
282
283
284 sub ol_fetch_workstation {
285         my $name = shift;
286         $logger->debug("offline: Fetching workstation $name");
287         my $ws = $U->storagereq(
288                 'open-ils.storage.direct.actor.workstation.search.name', $name);
289         ol_handle_result(OpenILS::Event->new('WORKSTATION_NOT_FOUND')) unless $ws;
290         return $ws;
291 }
292
293
294
295
296 # --------------------------------------------------------------------
297 # Sorts the script commands then forks a child to executes them.
298 # --------------------------------------------------------------------
299 sub ol_execute {
300
301         my $commands = ol_collect_commands();
302         
303         # --------------------------------------------------------------------
304         # Note that we must disconnect from opensrf before forking or the 
305         # connection will be borked...
306         # --------------------------------------------------------------------
307         my $con = OpenSRF::Transport::PeerHandle->retrieve;
308         $con->disconnect if $con;
309
310
311         if( safe_fork() ) {
312
313                 # --------------------------------------------------------------------
314                 # Tell the client all is well
315                 # --------------------------------------------------------------------
316                 ol_handle_event('SUCCESS'); # - this exits
317
318         } else {
319
320                 # --------------------------------------------------------------------
321                 # close stdout/stderr or apache will wait on the child to finish
322                 # --------------------------------------------------------------------
323                 close(STDOUT);
324                 close(STDERR);
325
326                 $logger->debug("offline: child $$ processing data...");
327
328                 # --------------------------------------------------------------------
329                 # The child re-connects to the opensrf network and processes
330                 # the script requests 
331                 # --------------------------------------------------------------------
332                 OpenSRF::System->bootstrap_client(config_file => $config{bootstrap});
333         
334                 try {
335                         ol_process_commands( $commands );
336                         ol_archive_files();
337
338                 } catch Error with {
339                         my $e = shift;
340                         $logger->error("offline: child process error $e");
341                 };
342         }
343 }
344
345 sub ol_file_to_perl {
346         my $fname = shift;
347         open(F, "$fname") or ol_handle_event('OFFLINE_FILE_ERROR');
348         my @d = <F>;
349         my @p;
350         push(@p, JSON->JSON2perl($_)) for @d;
351         close(F);
352         return \@p;
353 }
354
355 # collects the commands and sorts them on timestamp+delta
356 sub ol_collect_commands {
357         my $ses = ol_find_session();
358         my @commands;
359
360         # cycle through every script loaded to this session
361         for my $script ($ses->scripts) {
362                 my $coms = ol_file_to_perl($script->logfile);
363
364                 # cycle through all of the commands for this script
365                 for my $com (@$coms) {
366                         $$com{_worksation} = $script->workstation;
367                         $$com{_realtime} = $script->time_delta + $com->{timestamp};
368                         push( @commands, $com );
369                 }
370         }
371
372         # sort on realtime
373         @commands = sort { $a->{_realtime} <=> $b->{_realtime} } @commands;
374         return \@commands;
375 }
376
377 sub ol_process_commands {
378         my $commands = shift;
379         $logger->debug("offline: command = " . JSON->perl2JSON($_)) for @$commands;
380 }
381
382 sub ol_archive_files {
383 }
384
385
386
387
388