1 /*
2 * Copyright 2018 Oticon A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "bs_pc_base_types.h"
7 #include "bs_pc_base.h"
8 #include "bs_tracing.h"
9 #include "bs_oswrap.h"
10 #include "bs_string.h"
11 #include <signal.h>
12 #include <string.h>
13 #include <dirent.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <pwd.h>
20 #include <errno.h>
21
22
23 bool is_base_com_initialized = false; //Used by the BackChannel to avoid deadlocks
24 char *pb_com_path = NULL;
25 int pb_com_path_length = 0;
26
27 /**
28 * Create a FIFO if it doesn't exist
29 *
30 * Note that it may already exist, and/or that some other program
31 * may be racing to create at the same time
32 */
pb_create_fifo_if_not_there(const char * fifo_path)33 int pb_create_fifo_if_not_there(const char *fifo_path) {
34 if ((access(fifo_path, F_OK) == -1) && (mkfifo(fifo_path, S_IRWXG | S_IRWXU) != 0) && (access(fifo_path, F_OK) == -1)) {
35 bs_trace_warning_line("Can not create %s\n", fifo_path);
36 return -1;
37 }
38 return 0;
39 }
40
41 /**
42 * Create the comm folder if it doesn't exist
43 * And sets its name in cb_com_path
44 * Returns
45 * the length of the cb_com_path string if it succeeds
46 * -1 otherwise
47 */
pb_create_com_folder(const char * s)48 int pb_create_com_folder(const char *s) {
49 char *UserName = NULL;
50 int UserNameLength = 0;
51 struct passwd *pw;
52
53 uid_t euid = geteuid();
54 errno = 0;
55 pw = getpwuid(euid);
56 if (pw != NULL) {
57 UserName = pw->pw_name;
58 } else if (!((errno == 0) || (errno == ENOENT) || (errno== ESRCH) || (errno== EBADF) || (errno == EPERM))) {
59 bs_trace_warning_line("Warning, completely unexpected error trying to get the user name will try to recover but will likely fail"
60 "(euid=%i, errno=%i)\n", euid, errno);
61 } else {
62 //Otherwise (pw == NULL AND any of those errors), it seems the user is not a real user, or the connection to the NIS server is down or..
63 bs_trace_info_line(2, "You seem to be running this program as a not real user(?). Will try to recover"
64 "(euid=%i, errno=%i)\n", euid, errno);
65 }
66
67 if (UserName == NULL) {
68 //Let's hope the POSIX required LOGNAME environment variable is set
69 UserName = getenv("LOGNAME");
70 }
71 if (UserName == NULL) {
72 //It was not.. let's hope for USER..
73 UserName = getenv("USER");
74 }
75 if (UserName == NULL) {
76 bs_trace_error_line("Couldn't get the user name to build the tmp path for the interprocess comm (euid=%i)"
77 "(this shouldn't have happened, could not find the environment variables LOGNAME or USER either), errno=%i\n", euid, errno);
78 }
79
80 UserNameLength = strlen(UserName);
81
82 pb_com_path = (char*)bs_calloc(10 + strlen(s) + UserNameLength, sizeof(char));
83
84 sprintf(pb_com_path, "/tmp/bs_%s", UserName);
85
86 if (bs_createfolder(pb_com_path) != 0) {
87 free(pb_com_path);
88 pb_com_path = NULL;
89 return -1;
90 }
91 sprintf(&pb_com_path[8+UserNameLength], "/%s", s);
92 if (bs_createfolder(pb_com_path) != 0) {
93 free(pb_com_path);
94 pb_com_path = NULL;
95 return -1;
96 }
97 return 9 + UserNameLength + strlen(s);
98 }
99
pb_send_payload(int ff,void * buf,size_t size)100 void pb_send_payload(int ff, void *buf, size_t size) {
101 if (size) {
102 if (buf==NULL) {
103 bs_trace_error_line("Null pointer!!\n");
104 }
105 write(ff, buf, size);
106 }
107 }
108
109 //#define NO_LOCK_FILE
110
111 #if !defined(NO_LOCK_FILE)
lock_file_fill(const char * filename,long int my_pid)112 static void lock_file_fill(const char *filename, long int my_pid){
113 FILE *file;
114
115 file = bs_fopen(filename, "w");
116 fprintf(file, "%li\n",(long) my_pid);
117 #if defined(__linux)
118 long long unsigned int starttime = bs_get_process_start_time(my_pid);
119 fprintf(file,"%llu\n",starttime);
120 #endif
121 fclose(file);
122 }
123 #endif
124
125 /**
126 * Check if the lock file exists
127 * * If it doesn't create one for us
128 * * If the lock file exists:
129 * * If the process who created it is still running, we stop.
130 * * If the process who created it is not running, we print a warning and risk it
131 * * If we cannot determine who created it, we stop.
132 *
133 * Returns 0 if we consider it safe enough to continue
134 * Something else otherwise
135 */
test_and_create_lock_file(const char * filename)136 static int test_and_create_lock_file(const char *filename) {
137 #if !defined(NO_LOCK_FILE)
138 pid_t my_pid = getpid();
139
140 if (access(filename, F_OK) == -1) {
141 // The file does not exist. So unless somebody is racing us, we are safe to go
142 lock_file_fill(filename, my_pid);
143 return 0;
144 }
145
146 bs_trace_warning_line("Previous lock file found %s\n", filename);
147
148 FILE *file;
149 bool corrupt_file = false;
150 int other_dead = 1;
151 long int his_pid;
152 #if defined(__linux)
153 long long unsigned int his_starttime;
154 #endif
155
156 file = bs_fopen(filename, "r");
157 if ( fscanf(file, "%li\n", &his_pid) != 1 ) {
158 corrupt_file = true;
159 }
160 #if defined(__linux)
161 if ((corrupt_file == false) && (fscanf(file,"%llu\n", &his_starttime) != 1)) {
162 corrupt_file = true;
163 }
164 #endif
165
166 fclose(file);
167
168 if (corrupt_file) { //We are provably racing the other process, we stop
169 bs_trace_warning_line("Found previous lock owned by unknown process, we may be racing each other => aborting\n");
170 return 1;
171 }
172
173 other_dead = kill(his_pid, 0);
174
175 if (other_dead == 0) { //a process with the pid exists
176 #if defined(__linux)
177 /* To be sure the pid was not reused, let's check that the process start time matches */
178
179 uint64_t other_start_time = bs_get_process_start_time(his_pid);
180 if (his_starttime == other_start_time) { //it is the same process
181 bs_trace_warning_line("Found a previous, still RUNNING process w pid %li with the same sim_id and device port which would interfere with this one, aborting\n", his_pid);
182 } else {
183 other_dead = 1;
184 }
185 #else
186 bs_trace_warning_line("Found a previous, still RUNNING process w pid %li with the same sim_id and device port which would interfere with this one, aborting\n", his_pid);
187 #endif
188 }
189
190 if (other_dead){
191 bs_trace_warning_line("Found previous lock owned by DEAD process (pid was %li), will attempt to take over\n", his_pid);
192 lock_file_fill(filename, my_pid);
193 return 0;
194 } else {
195 return 1;
196 }
197
198 #else
199 return 0;
200 #endif
201 }
202
remove_lock_file(char ** file_path)203 static void remove_lock_file(char** file_path){
204 if (*file_path) {
205 remove(*file_path);
206 free(*file_path);
207 *file_path = NULL;
208 }
209 }
210
phy_test_and_create_lock_file(pb_phy_state_t * this,const char * phy_id)211 static int phy_test_and_create_lock_file(pb_phy_state_t *this, const char *phy_id){
212 int flen = pb_com_path_length + 20 + strlen(phy_id);
213 this->lock_path = (char*) bs_calloc(flen, sizeof(char));
214 sprintf(this->lock_path, "%s/%s.phy.lock", pb_com_path, phy_id);
215
216 int ret = test_and_create_lock_file(this->lock_path);
217 if (ret) {
218 free(this->lock_path);
219 this->lock_path = NULL;
220 }
221 return ret;
222 }
223
device_test_and_create_lock_file(pb_dev_state_t * this,const char * phy_id,unsigned int dev_nbr)224 static int device_test_and_create_lock_file(pb_dev_state_t *this, const char *phy_id, unsigned int dev_nbr){
225 int flen = pb_com_path_length + 20 + strlen(phy_id) + bs_number_strlen(dev_nbr);
226
227 this->lock_path = (char*) bs_calloc(flen, sizeof(char));
228
229 sprintf(this->lock_path, "%s/%s.d%i.lock", pb_com_path, phy_id, dev_nbr);
230 int ret = test_and_create_lock_file(this->lock_path);
231 if (ret) {
232 free(this->lock_path);
233 this->lock_path = NULL;
234 }
235 return ret;
236 }
237
238
239 /**
240 * Initialize the communication with the devices:
241 *
242 * inputs:
243 * this Pointer to structure where the connection status will be kept.
244 * MUST be initialized with zeroes.
245 * s String identifying the simulation
246 * p String identifying this phy in this simulation
247 * n How many devices we expect during the simulation
248 *
249 * returns:
250 * 0 if ok. Any other number on error
251 */
pb_phy_initcom(pb_phy_state_t * this,const char * s,const char * p,uint n)252 int pb_phy_initcom(pb_phy_state_t *this, const char* s, const char *p, uint n) {
253
254 signal(SIGPIPE, SIG_IGN);
255
256 if (this->device_connected) {
257 bs_trace_warning_line("%s called twice in a simulation\n", __func__);
258 return -1;
259 }
260
261 pb_com_path_length = pb_create_com_folder(s);
262
263 this->device_connected = NULL;
264 this->lock_path = NULL;
265
266 if ( phy_test_and_create_lock_file(this, p) ) {
267 return -1;
268 }
269
270 this->n_devices = n;
271 this->device_connected = (bool *) bs_calloc(n, sizeof(bool));
272 this->ff_path_dtp = (char **) bs_calloc(n, sizeof(char *));
273 this->ff_path_ptd = (char **) bs_calloc(n, sizeof(char *));
274 this->ff_dtp = (int *) bs_calloc(n, sizeof(int *));
275 this->ff_ptd = (int *) bs_calloc(n, sizeof(int *));
276
277 for (int d = 0; d < this->n_devices; d++) {
278 int flen = pb_com_path_length + 30 + strlen(p) + bs_number_strlen(d);
279 this->ff_path_dtp[d] = (char *)bs_calloc(flen, sizeof(char));
280 this->ff_path_ptd[d] = (char *)bs_calloc(flen, sizeof(char));
281 sprintf(this->ff_path_dtp[d], "%s/%s.d%i.dtp", pb_com_path, p, d);
282 sprintf(this->ff_path_ptd[d], "%s/%s.d%i.ptd", pb_com_path, p, d);
283
284 if ((pb_create_fifo_if_not_there(this->ff_path_dtp[d]) != 0)
285 || (pb_create_fifo_if_not_there(this->ff_path_ptd[d]) != 0)) {
286 pb_phy_disconnect_devices(this);
287 bs_trace_error_line("Could not create FIFOs to device %i\n", d);
288 }
289
290 if ((this->ff_ptd[d] = open(this->ff_path_ptd[d], O_WRONLY)) == -1) {
291 this->ff_ptd[d] = 0;
292 pb_phy_disconnect_devices(this);
293 bs_trace_error_line("Opening FIFO from phy to device %i failed\n", d);
294 }
295 if ((this->ff_dtp[d] = open(this->ff_path_dtp[d], O_RDONLY)) == -1) {
296 this->ff_dtp[d] = 0;
297 pb_phy_disconnect_devices(this);
298 bs_trace_error_line("Opening FIFO from device %i to phy failed\n", d);
299 }
300
301 this->device_connected[d] = true;
302 }
303
304 return 0;
305 }
306
pb_phy_free_one_device(pb_phy_state_t * this,int d)307 void pb_phy_free_one_device(pb_phy_state_t *this, int d) {
308 if (this->ff_dtp[d]) {
309 close(this->ff_dtp[d]);
310 this->ff_dtp[d] = 0;
311 }
312 if (this->ff_path_dtp[d]) {
313 remove(this->ff_path_dtp[d]);
314 free(this->ff_path_dtp[d]);
315 this->ff_path_dtp[d] = NULL;
316 }
317 if (this->ff_ptd[d]) {
318 close(this->ff_ptd[d]);
319 this->ff_ptd[d] = 0;
320 }
321 if (this->ff_path_ptd[d]) {
322 remove(this->ff_path_ptd[d]);
323 free(this->ff_path_ptd[d]);
324 this->ff_path_ptd[d] = NULL;
325 }
326 this->device_connected[d] = false;
327 }
328
329 /**
330 * Disconnect all devices we are still connected to,
331 * free all the memory we used and delete the FIFOs
332 *
333 * It is safe to call this function multiple times
334 */
pb_phy_disconnect_devices(pb_phy_state_t * this)335 void pb_phy_disconnect_devices(pb_phy_state_t *this) {
336
337 if (this->device_connected != NULL) {
338 pc_header_t header = PB_MSG_DISCONNECT;
339 for (int d = 0; d < this->n_devices; d++) {
340 if (this->ff_ptd[d]) {
341 write(this->ff_ptd[d], &header, sizeof(header));
342 }
343 pb_phy_free_one_device(this, d);
344 }
345
346 if (pb_com_path) {
347 rmdir(pb_com_path);
348 free(pb_com_path);
349 pb_com_path = NULL;
350 }
351
352 if (this->device_connected) {
353 free(this->device_connected);
354 this->device_connected = NULL;
355 }
356 if (this->ff_path_dtp) {
357 free(this->ff_path_dtp);
358 this->ff_path_dtp = NULL;
359 }
360 if (this->ff_dtp) {
361 free(this->ff_dtp);
362 this->ff_dtp = NULL;
363 }
364 if (this->ff_path_ptd) {
365 free(this->ff_path_ptd);
366 this->ff_path_ptd = NULL;
367 }
368 if (this->ff_ptd) {
369 free(this->ff_ptd);
370 this->ff_ptd = NULL;
371 }
372 }
373 remove_lock_file(&this->lock_path);
374 }
375
376 /**
377 * Check if we are connected to this device (or any device)
378 * Return 1 if we are
379 */
pb_phy_is_connected_to_device(pb_phy_state_t * this,uint d)380 int pb_phy_is_connected_to_device(pb_phy_state_t *this, uint d){
381 if ((this->device_connected == NULL) || (!this->device_connected[d])) {
382 bs_trace_error_line("Programming error while trying to talk to device %i\n", d);
383 return 0;
384 }
385 return 1;
386 }
387
388 /**
389 * Respond to the device at the end of wait
390 */
pb_phy_resp_wait(pb_phy_state_t * this,uint d)391 void pb_phy_resp_wait(pb_phy_state_t *this, uint d) {
392 if ( pb_phy_is_connected_to_device(this, d) ) {
393 pc_header_t header = PB_MSG_WAIT_END;
394 write(this->ff_ptd[d], &header, sizeof(header));
395 }
396 }
397
398 /**
399 * Get (and return) the next request from this device
400 */
pb_phy_get_next_request(pb_phy_state_t * this,uint d)401 pc_header_t pb_phy_get_next_request(pb_phy_state_t *this, uint d) {
402 pc_header_t header = PB_MSG_DISCONNECT;
403
404 if ( pb_phy_is_connected_to_device(this, d) ) {
405 int n = read(this->ff_dtp[d], &header, sizeof(header));
406 if (n < sizeof(header)) {
407 bs_trace_warning_line("Device %u left the party unsuspectingly.. I treat it as if it disconnected\n", d);
408 }
409
410 if ((header == PB_MSG_DISCONNECT) || (header == PB_MSG_TERMINATE)) {
411 //if the read failed or the device really wants to disconnect
412 pb_phy_free_one_device(this, d);
413 }
414 }
415 return header;
416 }
417
pb_phy_get_wait_s(pb_phy_state_t * this,uint d,pb_wait_t * wait_s)418 void pb_phy_get_wait_s(pb_phy_state_t *this, uint d, pb_wait_t *wait_s) {
419 if ( pb_phy_is_connected_to_device(this, d) ) {
420 read(this->ff_dtp[d], wait_s, sizeof(pb_wait_t));
421 }
422 }
423
424 /**
425 * Initialize the communication interface with the phy
426 *
427 * inputs:
428 * this Pointer to structure where the connection status will be keps.
429 * MUST be initialized with zeroes.
430 * d The device number this device will have in this phy
431 * s String identifying the simulation
432 * p String identifying this phy in this simulation
433 *
434 * returns:
435 * 0 if ok. Any other number on error
436 */
pb_dev_init_com(pb_dev_state_t * this,uint d,const char * s,const char * p)437 int pb_dev_init_com(pb_dev_state_t *this, uint d, const char* s, const char *p) {
438
439 if (this->connected) {
440 bs_trace_warning_line("%s called twice in a simulation\n", __func__);
441 return -1;
442 }
443
444 /* In case we fail, we initialize them to "invalid" content*/
445 this->ff_path_dtp = NULL;
446 this->ff_path_ptd = NULL;
447
448 this->ff_ptd = 0; /*0 == stdin == not one we would have used */
449 this->ff_dtp = 0;
450
451 if ((s == NULL) || (p == NULL)) {
452 bs_trace_error_line("The simulation and phy identification strings need to be provided\n");
453 }
454
455 this->this_dev_nbr = d;
456 pb_com_path_length = pb_create_com_folder(s);
457
458 if ( device_test_and_create_lock_file(this, p, d) ) {
459 bs_trace_error_line("Failed to get lock\n");
460 }
461
462 int flen = pb_com_path_length + strlen(p) + bs_number_strlen(d) + 30;
463 this->ff_path_dtp = (char *) bs_calloc(flen, sizeof(char));
464 this->ff_path_ptd = (char *) bs_calloc(flen, sizeof(char));
465 sprintf(this->ff_path_dtp, "%s/%s.d%i.dtp", pb_com_path, p, d);
466 sprintf(this->ff_path_ptd, "%s/%s.d%i.ptd", pb_com_path, p, d);
467
468 if ((pb_create_fifo_if_not_there(this->ff_path_dtp) != 0)
469 || (pb_create_fifo_if_not_there(this->ff_path_ptd) != 0)) {
470 pb_dev_clean_up(this);
471 bs_trace_error_line("Could not create FIFOs");
472 }
473
474 if (((this->ff_ptd = open(this->ff_path_ptd, O_RDONLY )) == -1)) {
475 this->ff_ptd = 0;
476 pb_dev_clean_up(this);
477 bs_trace_error_line("Opening FIFO from phy to device failed\n");
478 }
479 if (((this->ff_dtp = open(this->ff_path_dtp, O_WRONLY )) == -1)) {
480 this->ff_dtp = 0;
481 pb_dev_clean_up(this);
482 bs_trace_error_line("Opening FIFO from device to phy failed\n");
483 }
484
485 this->connected = true;
486 is_base_com_initialized = true;
487 return 0;
488 }
489
490 /**
491 * Attempt to terminate the simulation and disconnect
492 */
pb_dev_terminate(pb_dev_state_t * this)493 void pb_dev_terminate(pb_dev_state_t *this) {
494 if (this->connected) {
495 pc_header_t header = PB_MSG_TERMINATE;
496
497 write(this->ff_dtp, &header, sizeof(header));
498 pb_dev_clean_up(this);
499 }
500 }
501
502 /**
503 * Disconnect from the phy
504 */
pb_dev_disconnect(pb_dev_state_t * this)505 void pb_dev_disconnect(pb_dev_state_t *this) {
506 if (this->connected) {
507 pc_header_t header = PB_MSG_DISCONNECT;
508
509 write(this->ff_dtp, &header, sizeof(header));
510 pb_dev_clean_up(this);
511 }
512 }
513
514 /*
515 * Try to delete the FIFOs, clear memory and try to delete the directory
516 *
517 * It is safe to call this function (unnecessarily) several times
518 */
pb_dev_clean_up(pb_dev_state_t * this)519 void pb_dev_clean_up(pb_dev_state_t *this) {
520
521 remove_lock_file(&this->lock_path);
522
523 this->connected = false; //we don't want any possible future call to libphycom to attempt to talk with the phy
524
525 if (this->ff_path_dtp) {
526 if (this->ff_dtp) {
527 close(this->ff_dtp);
528 this->ff_dtp = 0;
529 }
530
531 remove(this->ff_path_dtp);
532 free(this->ff_path_dtp);
533 this->ff_path_dtp = NULL;
534 }
535
536 if (this->ff_path_ptd) {
537 if (this->ff_ptd) {
538 close(this->ff_ptd);
539 this->ff_ptd = 0;
540 }
541 remove(this->ff_path_ptd);
542 free(this->ff_path_ptd);
543 this->ff_path_ptd = NULL;
544 }
545
546 if (pb_com_path != NULL) {
547 rmdir(pb_com_path);
548 free(pb_com_path);
549 pb_com_path = NULL;
550 }
551 }
552
553 /**
554 * Read from a FIFO n_bytes
555 * returns -1 on failure (it can't read n_bytes, and cleans up),
556 * otherwise returns n_bytes
557 */
pb_dev_read(pb_dev_state_t * this,void * buf,size_t n_bytes)558 int pb_dev_read(pb_dev_state_t *this, void *buf, size_t n_bytes) {
559 int read_b;
560
561 read_b = read(this->ff_ptd, buf, n_bytes);
562
563 if (n_bytes == read_b) {
564 return read_b;
565 }
566
567 bs_trace_warning_line(COM_FAILED_ERROR " (tried to get %i got %i bytes)\n", n_bytes, read_b);
568 pb_dev_clean_up(this);
569 return -1;
570 }
571
572 /**
573 * Request a non blocking wait to the phy
574 * Note that eventually the caller needs to pick the wait response
575 * from the phy with cb_dev_pick_wait_resp()
576 */
pb_dev_request_wait_nonblock(pb_dev_state_t * this,pb_wait_t * wait_s)577 int pb_dev_request_wait_nonblock(pb_dev_state_t *this, pb_wait_t *wait_s) {
578 CHECK_CONNECTED(this->connected);
579 pb_send_msg(this->ff_dtp, PB_MSG_WAIT, (void *)wait_s, sizeof(pb_wait_t));
580 return 0;
581 }
582
583 /**
584 * Block until getting a wait response from the phy
585 * If everything goes ok, the phy has just reached the
586 * requested end time and 0 is returned
587 * Otherwise, we should disconnect (-1 will be returned)
588 */
pb_dev_pick_wait_resp(pb_dev_state_t * this)589 int pb_dev_pick_wait_resp(pb_dev_state_t *this) {
590 CHECK_CONNECTED(this->connected);
591
592 pc_header_t header = PB_MSG_DISCONNECT;
593
594 if (pb_dev_read(this, &header, sizeof(header)) == -1) {
595 return -1;
596 }
597
598 if (header == PB_MSG_DISCONNECT) {
599 pb_dev_clean_up(this);
600 return -1;
601 } else if (header == PB_MSG_WAIT_END) {
602 return 0;
603 } else {
604 INVALID_RESP(header);
605 return -1;
606 }
607 }
608
609 /**
610 * Request a wait to the phy and block until receiving the response
611 * If everything goes ok 0 is returned
612 *
613 * Otherwise, we should disconnect (-1 will be returned)
614 */
pb_dev_request_wait_block(pb_dev_state_t * this,pb_wait_t * wait_s)615 int pb_dev_request_wait_block(pb_dev_state_t *this, pb_wait_t *wait_s) {
616 CHECK_CONNECTED(this->connected);
617 int ret;
618 ret = pb_dev_request_wait_nonblock(this, wait_s);
619 if (ret)
620 return ret;
621 return pb_dev_pick_wait_resp(this);
622 }
623