1 /*
2 This file is part of GNU APL, a free implementation of the
3 ISO/IEC Standard 13751, "Programming Language APL, Extended"
4
5 Copyright (C) 2008-2013 Dr. Jürgen Sauermann
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <errno.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/wait.h>
28 #include <sys/time.h>
29 #include <unistd.h>
30
31 #include <cassert>
32 #include <iomanip>
33 #include <iostream>
34 #include <fstream>
35 #include <vector>
36
37 #include "APmain.hh"
38 #include "Common.hh"
39 #include "PrintOperator.hh"
40 #include "Svar_DB.hh"
41 #include "Svar_signals.hh"
42
43 #ifdef DYNAMIC_LOG_WANTED
44 extern bool LOG_startup;
45 extern bool LOG_shared_variables;
46 extern bool LOG_Svar_DB_signals;
47 bool LOG_startup = false;
48 bool LOG_shared_variables = false;
49 bool LOG_Svar_DB_signals = false;
50 #else
51 #endif
52
53 extern const char * prog_name();
54
55 //-----------------------------------------------------------------------------
56
57 bool verbose = false;
58 int event_port = 0;
59
60 const char * prog = 0;
61 char pref[FILENAME_MAX + 20];
62
63 /// coupled variables
64 vector<Coupled_var> coupled_vars;
65
66 /// the place where the AP specific part has detected an error
67 string error_loc = "?";
68
69 /// the name of this AP (aplXXX where XXX is the processor number)
70 char AP_NAME[40] = "ap" STR(AP_NUM);
71
72 AP_num3 ProcessorID::id(NO_AP, AP_NULL, AP_NULL);
73
get_CERR()74 ostream & get_CERR()
75 {
76 return cerr;
77 }
78 //-----------------------------------------------------------------------------
79 uint64_t
now_ms()80 now_ms()
81 {
82 timeval current;
83 gettimeofday(¤t, 0);
84
85 uint64_t ret = current.tv_sec;
86 ret *= 1000;
87 ret += current.tv_usec/1000;
88 return ret;
89 }
90 //-----------------------------------------------------------------------------
91 /// return true if key already exists; otherwise add variable and return false
92 bool
add_var(SV_key key)93 add_var(SV_key key)
94 {
95 for (size_t c = 0; c < coupled_vars.size(); ++c)
96 {
97 Coupled_var & cv = coupled_vars[c];
98 if (key == cv.key) return true; // key already exists
99 }
100
101 // key is new; add it to coupled_vars
102 //
103 Coupled_var cv = { key, 0, 0 };
104 initialize(cv);
105 coupled_vars.push_back(cv);
106
107 return false;
108 }
109 //-----------------------------------------------------------------------------
110 void
print_vars(ostream & out)111 print_vars(ostream & out)
112 {
113 for (size_t c = 0; c < coupled_vars.size(); ++c)
114 {
115 Coupled_var & cv = coupled_vars[c];
116 get_CERR() << " key: 0x" << hex << cv.key << dec << " ";
117 const uint32_t * varname = Svar_DB::get_svar_name(cv.key);
118 if (varname)
119 {
120 while (*varname) get_CERR() << (Unicode)(*varname++);
121 }
122 else
123 {
124 get_CERR() << "(unknown var)";
125 }
126 get_CERR() << endl;
127 }
128 }
129 //-----------------------------------------------------------------------------
130 void
do_Assert(const char * cond,const char * fun,const char * file,int line)131 do_Assert(const char * cond, const char * fun, const char * file, int line)
132 {
133 get_CERR() << "Assertion '" << cond << "' failed at "
134 << file << ":" << line << endl;
135
136 assert(0);
137 }
138 //-----------------------------------------------------------------------------
139 void
control_C(int)140 control_C(int)
141 {
142 AP_num3 this_proc = ProcessorID::get_id();
143 if (verbose) get_CERR() << pref << " unregistering processor "
144 << this_proc << endl;
145
146 if (verbose) get_CERR() << pref << " done (got ^C)" << endl;
147
148 exit(0);
149 }
150
151 static struct sigaction old_ctl_C_action;
152 static struct sigaction new_ctl_C_action;
153
154 //-----------------------------------------------------------------------------
usage()155 int usage()
156 {
157 get_CERR()
158 << "Usage:" << endl
159 << prog << " [options]" << endl
160 << endl
161 << "options:" << endl
162 << " --auto automatically started (exit after last retract)" << endl
163 << " --id num run as processor num" << endl
164 << " --par num run as child of num" << endl
165 << " --gra num run as grandchild of num" << endl
166 << " -h, --help print this help" << endl
167 << " -v verbose" << endl
168 << endl;
169
170 return 1;
171 }
172 //-----------------------------------------------------------------------------
173 TCP_socket tcp2 = NO_TCP_SOCKET;
174
175 TCP_socket
get_TCP_for_key(SV_key key)176 get_TCP_for_key(SV_key key)
177 {
178 return tcp2;
179 }
180 //-----------------------------------------------------------------------------
181 int
main(int argc,char * argv[])182 main(int argc, char * argv[])
183 {
184 bool need_help = false;
185 bool auto_started = false;
186
187 prog = argv[0];
188 char bin_path[FILENAME_MAX];
189 strncpy(bin_path, prog, sizeof(bin_path));
190
191 char * slash = strrchr(bin_path, '/');
192 if (slash)
193 {
194 prog = slash + 1;
195 *slash = 0;
196 }
197
198 Svar_DB::init(bin_path, prog, /* retry_max */ 10, /* logit */ false,
199 /* do_svars */ true);
200
201 if (strrchr(prog, '/')) prog = strrchr(prog, '/') + 1;
202 snprintf(pref, sizeof(pref) - 1, "%s(%u) ", prog, getpid());
203
204 // set default processor ID
205 ProcessorID::set_own_ID(AP_num(AP_NUM));
206
207 for (int a = 1; a < argc; )
208 {
209 const char * opt = argv[a++];
210 const char * val = (a < argc) ? argv[a] : 0;
211
212 if (!strcmp(opt, "-h")) need_help = true;
213 else if (!strcmp(opt, "--help")) need_help = true;
214 else if (!strcmp(opt, "--auto")) auto_started = true;
215 else if (!strcmp(opt, "-v")) verbose = true;
216 else if (!strcmp(opt, "--id") && val)
217 { ProcessorID::set_own_ID(AP_num(atoi(val))); ++a; }
218 else if (!strcmp(opt, "--par") && val)
219 { ProcessorID::set_parent_ID(AP_num(atoi(val))); ++a; }
220 else if (!strcmp(opt, "--gra") && val)
221 { ProcessorID::set_grand_ID(AP_num(atoi(val))); ++a; }
222 else
223 {
224 get_CERR() << pref << ": Bad command line option '"
225 << argv[a] << "'" << endl;
226 need_help = true;
227 }
228 }
229
230 if (need_help) return usage();
231
232 snprintf(AP_NAME, sizeof(AP_NAME), "apl%u", ProcessorID::get_id().proc);
233
234 // serious attempt to run: run in the background
235 //
236 if (fork()) return 0; // parent returns (daemonize)
237
238 // child code...
239 // register as e.g. AP210-port. We do this BEFORE closing stdout so that
240 // caller of our parent waits until we have closed stdout
241 //
242
243 memset(&new_ctl_C_action, 0, sizeof(struct sigaction));
244 new_ctl_C_action.sa_handler = &control_C;
245 sigaction(SIGTERM, &new_ctl_C_action, &old_ctl_C_action);
246
247 Svar_partner this_proc(ProcessorID::get_id(), NO_TCP_SOCKET);
248
249 // tcp is the "normal" connection to APserver as server
250 // tcp2 is a special connection on which APserver (as client) can
251 // send events to this process.
252 //
253 const TCP_socket tcp = Svar_DB::get_DB_tcp();
254
255 if (ProcessorID::get_id().proc < AP_FIRST_USER)
256 tcp2 = Svar_DB::connect_to_APserver(0, prog_name(), /* retry_max */ 10,
257 verbose);
258
259 if (tcp == NO_TCP_SOCKET || tcp2 == NO_TCP_SOCKET)
260 {
261 cerr << prog_name() << ":*** connection to APserver failed" << endl;
262 return 3;
263 }
264
265 string progname(prog_name());
266
267 { REGISTER_PROCESSOR_c request(tcp, this_proc.id.proc,
268 this_proc.id.parent,
269 this_proc.id.grand,
270 0, progname);
271 }
272
273 if (this_proc.id.proc < AP_FIRST_USER)
274 { progname += "-EV";
275 REGISTER_PROCESSOR_c request(tcp2, this_proc.id.proc,
276 this_proc.id.parent,
277 this_proc.id.grand,
278 1, progname);
279 }
280
281 fclose(stdout); // cause getc() of caller to return EOF !
282
283 for (bool goon = true; goon;)
284 {
285 uint8_t buff[MAX_SIGNAL_CLASS_SIZE + 40000];
286 char * del = 0;
287 const Signal_base * signal = Signal_base::recv_TCP(tcp2, (char *)buff,
288 sizeof(buff), del, 0);
289
290 if (signal == 0) // no signal for 10 seconds
291 {
292 goon = false;
293 if (verbose) get_CERR() << AP_NAME
294 << " done (parent died)" << endl;
295 continue;
296 }
297
298 #if 0
299 #if AP_NUM == 0
300 cerr << "APnnn got " << signal->get_sigName() << endl;
301 #endif
302 #endif
303 switch(signal->get_sigID())
304 {
305 case sid_MAKE_OFFER: // a new offer from a peer
306 if (verbose) get_CERR() << AP_NAME
307 << " got MAKE_OFFER" << endl;
308 {
309 const SV_key key = signal->get__MAKE_OFFER__key();
310 const uint32_t * varname = Svar_DB::get_svar_name(key);
311 if (varname == 0)
312 {
313 get_CERR() << "Could not find svar for key "
314 << key << " at " << LOC << endl;
315 break;
316 }
317
318 if (! is_valid_varname(varname))
319 {
320 get_CERR() << "Bad varname ";
321 while (*varname) get_CERR() << (Unicode)(*varname++);
322 get_CERR() << " at " << LOC << endl;
323 break;
324 }
325
326 // make a counter offer (APs 100 and 210) or not (APnnn)
327 //
328 if (!make_counter_offer(key)) break; // APnnn
329
330 const AP_num3 offering_id = Svar_DB::find_offering_id(key);
331
332 Svar_DB::match_or_make(varname, offering_id, this_proc);
333
334 add_var(key);
335
336 Svar_DB::set_state(key, true, LOC);
337 }
338 break;
339
340 case sid_RETRACT_OFFER: // ⎕SVR varname
341 verbose && get_CERR() << AP_NAME
342 << " got RETRACT_OFFER" << endl;
343 {
344 const SV_key key = signal->get__RETRACT_OFFER__key();
345 for (size_t c = 0; c < coupled_vars.size(); ++c)
346 {
347 Coupled_var & cv = coupled_vars[c];
348 if (key == cv.key)
349 {
350 retract(cv);
351 coupled_vars.erase(coupled_vars.begin() + c);
352
353 if (coupled_vars.size() == 0 && auto_started)
354 {
355 if (verbose) get_CERR() << AP_NAME << " done"
356 " (last variable retracted)" << endl;
357 goon = false;
358 break;
359 }
360 }
361 }
362 Svar_DB::add_event(key, ProcessorID::get_id(),
363 SVE_OFFER_RETRACT);
364 }
365 break;
366
367 case sid_GET_VALUE:
368 if (verbose) get_CERR() << AP_NAME
369 << " got GET_VALUE" << endl;
370 {
371 const SV_key key = signal->get__GET_VALUE__key();
372 APL_error_code error = E_VALUE_ERROR;
373 bool found = false;
374 string data;
375 for (size_t c = 0; c < coupled_vars.size(); ++c)
376 {
377 Coupled_var & cv = coupled_vars[c];
378 if (key == cv.key)
379 {
380 found = true;
381 error_loc = LOC; error = get_value(cv, data);
382 break;
383 }
384 }
385
386 if (!found)
387 {
388 get_CERR() << "Key 0x" << hex << key << dec
389 << " not found. Variables are:"
390 << endl;
391 print_vars(get_CERR());
392 error_loc = LOC; error = E_VALUE_ERROR;
393 }
394
395 VALUE_IS_c response(tcp2, key, error, error_loc, data);
396 }
397 break;
398
399 case sid_ASSIGN_VALUE:
400 verbose && get_CERR() << AP_NAME
401 << " got ASSIGN_VALUE" << endl;
402 {
403 const SV_key key = signal->get__ASSIGN_VALUE__key();
404 APL_error_code error = E_VALUE_ERROR;
405 bool found = false;
406 for (size_t c = 0; c < coupled_vars.size(); ++c)
407 {
408 Coupled_var & cv = coupled_vars[c];
409 if (key == cv.key)
410 {
411 found = true;
412 error_loc = LOC; error = assign_value(cv,
413 signal->get__ASSIGN_VALUE__cdr_value());
414 break;
415 }
416 }
417
418 if (!found)
419 {
420 get_CERR() << "Key 0x" << hex << key << dec
421 << " not found. Variables are:"
422 << endl;
423 print_vars(get_CERR());
424 error_loc = LOC; error = E_VALUE_ERROR;
425 }
426
427 SVAR_ASSIGNED_c response(tcp, key, error, error_loc);
428 }
429 break;
430
431 default: get_CERR() << pref << ": bad signal ID "
432 << signal->get_sigID() << " ("
433 << signal->get_sigName() << ")" << endl;
434 }
435
436 if (del) delete del;
437 }
438
439 return 0;
440 }
441 //-----------------------------------------------------------------------------
operator <<(ostream & out,const AP_num3 & ap3)442 ostream & operator << (ostream & out, const AP_num3 & ap3)
443 {
444 return out << ap3.proc << "." << ap3.parent << "." << ap3.grand;
445 }
446 //-----------------------------------------------------------------------------
447 ostream &
operator <<(ostream & os,Unicode uni)448 operator << (ostream & os, Unicode uni)
449 {
450 if (uni < 0x80) return os << (char)uni;
451
452 if (uni < 0x800) return os << (char)(0xC0 | (uni >> 6))
453 << (char)(0x80 | (uni & 0x3F));
454
455 if (uni < 0x10000) return os << (char)(0xE0 | (uni >> 12))
456 << (char)(0x80 | (uni >> 6 & 0x3F))
457 << (char)(0x80 | (uni & 0x3F));
458
459 if (uni < 0x200000) return os << (char)(0xF0 | (uni >> 18))
460 << (char)(0x80 | (uni >> 12 & 0x3F))
461 << (char)(0x80 | (uni >> 6 & 0x3F))
462 << (char)(0x80 | (uni & 0x3F));
463
464 if (uni < 0x4000000) return os << (char)(0xF8 | (uni >> 24))
465 << (char)(0x80 | (uni >> 18 & 0x3F))
466 << (char)(0x80 | (uni >> 12 & 0x3F))
467 << (char)(0x80 | (uni >> 6 & 0x3F))
468 << (char)(0x80 | (uni & 0x3F));
469
470 return os << (char)(0xFC | (uni >> 30))
471 << (char)(0x80 | (uni >> 24 & 0x3F))
472 << (char)(0x80 | (uni >> 18 & 0x3F))
473 << (char)(0x80 | (uni >> 12 & 0x3F))
474 << (char)(0x80 | (uni >> 6 & 0x3F))
475 << (char)(0x80 | (uni & 0x3F));
476 }
477 //-----------------------------------------------------------------------------
478
479