Mon 16 Mar 11:09:06 CET 2026
This commit is contained in:
parent
e9a642eb1b
commit
3725ba9c13
477
src/unix.c
Normal file
477
src/unix.c
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
#include "config.h"
|
||||
#include "types.h"
|
||||
#include "lexer.h"
|
||||
#include "tokens.h"
|
||||
#include "mem.h"
|
||||
#include "reg.h"
|
||||
#include "stack.h"
|
||||
#include "context.h"
|
||||
#include "ops.h"
|
||||
#include "code.h"
|
||||
#include "vm.h"
|
||||
#include "var.h"
|
||||
#include "printf.h"
|
||||
#include "ccall.h"
|
||||
#include "event.h"
|
||||
#include "context.h"
|
||||
#include "version.h"
|
||||
#include "unix.h"
|
||||
#include "utils.h"
|
||||
#include <sys/select.h>
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define __stack_t_defined 1
|
||||
#include <time.h> /* for timer functions and itimerspec */
|
||||
#include <signal.h> /* for sigaction */
|
||||
#include <errno.h> /* for errno */
|
||||
|
||||
#include "net.h"
|
||||
#include <poll.h>
|
||||
|
||||
|
||||
extern int verbose;
|
||||
|
||||
/* Makes the given file descriptor non-blocking.
|
||||
* Returns 1 on success, 0 on failure.
|
||||
*/
|
||||
int fd_make_blocking(int fd)
|
||||
{
|
||||
int flags;
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
if(flags == -1) /* Failed? */
|
||||
return 0;
|
||||
/* Clear the blocking flag. */
|
||||
flags &= ~O_NONBLOCK;
|
||||
return fcntl(fd, F_SETFL, flags) != -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
IO Multiplexer
|
||||
==============
|
||||
Low-level IO via file descriptors (fd>=0) by net module or host application
|
||||
High-level IO via ports from VM and PLX IO operations !? (fd<0)
|
||||
*/
|
||||
int availbyte(int fd) {
|
||||
if (fd>=0) {
|
||||
// real fd
|
||||
struct pollfd pfd;
|
||||
pfd.fd=fd;
|
||||
pfd.events=POLLIN;
|
||||
poll(&pfd,1,0);
|
||||
return pfd.revents&POLLIN?1:0;
|
||||
} else {
|
||||
fd=-fd-1;
|
||||
#if HAS_NET > 0
|
||||
#if HAS_NET_ROUTING > 0
|
||||
|
||||
#else /* HAS_NET_ROUTING=0 */
|
||||
if (Port && (fd==0 || fd==1000)) {
|
||||
// Port 0
|
||||
return PortAvailByte(0);
|
||||
}
|
||||
#endif
|
||||
#else /* HAS_NET=0 */
|
||||
#endif
|
||||
if (fd==0) {
|
||||
struct pollfd pfd;
|
||||
pfd.fd=fd;
|
||||
pfd.events=POLLIN;
|
||||
poll(&pfd,1,0);
|
||||
return pfd.revents&POLLIN?1:0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
char inbyte(int fd) {
|
||||
char buf=0;
|
||||
if (fd>=0) {
|
||||
read(fd,&buf,1);
|
||||
return buf;
|
||||
} else {
|
||||
fd=-fd-1;
|
||||
#if HAS_NET > 0
|
||||
#if HAS_NET_ROUTING > 0
|
||||
|
||||
#else /* HAS_NET_ROUTING=0 */
|
||||
if (Port && (fd==0 || fd==1000)) {
|
||||
// Port 0
|
||||
return PortInByte(0);
|
||||
}
|
||||
#endif
|
||||
#else /* HAS_NET=0 */
|
||||
#endif
|
||||
if (fd==0) return getchar();
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
int outbyte(int fd,char x) {
|
||||
if (fd>=0) {
|
||||
return write(fd,&x,1);
|
||||
} else {
|
||||
fd=-fd-1;
|
||||
#if HAS_NET > 0
|
||||
#if HAS_NET_ROUTING > 0
|
||||
|
||||
#else /* HAS_NET_ROUTING=0 */
|
||||
if (Port && (fd==1 || fd==1000)) {
|
||||
// Port 0
|
||||
return PortOutByte(0,x);
|
||||
}
|
||||
#endif
|
||||
#else /* HAS_NET=0 */
|
||||
#endif
|
||||
if (fd==1) putchar(x);
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
extern int verbose;
|
||||
#if HAS_NET > 0
|
||||
/*
|
||||
Under UNIX we use named FS FIFOs for node-node communication.
|
||||
The FIFOs are connected to links.
|
||||
*/
|
||||
static int portfdrd[NUMPORTS];
|
||||
static int portfdwr[NUMPORTS];
|
||||
char *portfilerd[NUMPORTS];
|
||||
char *portfilewr[NUMPORTS];
|
||||
static struct pollfd portfds[NUMPORTS]; // currently only rx monitors
|
||||
static int nportfds; // number of polled (rx) fds
|
||||
|
||||
/*
|
||||
Open all registered fifo ports
|
||||
*/
|
||||
|
||||
int port_open() {
|
||||
for(int i=0;i<NUMPORTS;i++) {
|
||||
if (portfilerd[i]) {
|
||||
mkfifo(portfilerd[i],0666);
|
||||
portfdrd[i]=open(portfilerd[i], O_RDONLY|O_NONBLOCK);
|
||||
if (portfdrd[i] < 0) {
|
||||
perror("open portrd");
|
||||
exit(1);
|
||||
}
|
||||
portfds[i].fd=portfdrd[i];
|
||||
portfds[i].events=POLLIN|POLLHUP;
|
||||
nportfds++;
|
||||
log(LOGINFO,"Read port fifo %s opened (%d).\n",portfilerd[i],portfdrd[i]);
|
||||
#if HAS_NET_ROUTING > 0
|
||||
Ports[i]->rd=portfdrd[i];
|
||||
Ports[i]->state=LSTART;
|
||||
#else
|
||||
Port->rd=portfdrd[i];
|
||||
Port->rxState=LSTART;
|
||||
#endif
|
||||
}
|
||||
if (portfilewr[i]) {
|
||||
mkfifo(portfilewr[i],0666);
|
||||
portfdwr[i]=open(portfilewr[i], O_RDWR);
|
||||
if (portfdwr[i] < 0) {
|
||||
perror("open portwr");
|
||||
exit(1);
|
||||
}
|
||||
log(LOGINFO,"Write port fifo %s opened (%d).\n",portfilewr[i],portfdwr[i]);
|
||||
#if HAS_NET_ROUTING > 0
|
||||
Ports[i]->wr=portfdwr[i];
|
||||
#else
|
||||
Port->wr=portfdwr[i];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Got HUP from other side, reopen port fifo
|
||||
*/
|
||||
int port_reopen(int portindex, char dir) {
|
||||
if (dir=='r' && portfilerd[portindex]) {
|
||||
close(portfdrd[portindex]);
|
||||
portfdrd[portindex]=open(portfilerd[portindex], O_RDONLY|O_NONBLOCK);
|
||||
if (portfdrd[portindex] < 0) {
|
||||
perror("reopen portrd");
|
||||
exit(1);
|
||||
}
|
||||
log(LOGINFO,"Read port fifo %s reopened (%d).\n",portfilerd[portindex],portfdrd[portindex]);
|
||||
portfds[portindex].fd=portfdrd[portindex];
|
||||
portfds[portindex].events=POLLIN|POLLHUP;
|
||||
}
|
||||
if (dir=='w' && portfilewr[portindex]) {
|
||||
close(portfdwr[portindex]);
|
||||
portfdwr[portindex]=open(portfilewr[portindex], O_RDWR);
|
||||
if (portfdwr[portindex] < 0) {
|
||||
perror("reopen portwr");
|
||||
exit(1);
|
||||
}
|
||||
log(LOGINFO,"Write port fifo %s reopened (%d).\n",portfilewr[portindex],portfdwr[portindex]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Close all opened ports (on exit)
|
||||
*/
|
||||
void port_close(void) {
|
||||
if (verbose) printf("closing ...\n");
|
||||
for(int i=0;i<NUMPORTS;i++) {
|
||||
if (portfilerd[i]) {
|
||||
// close(portfdrd[i]);
|
||||
unlink(portfilerd[i]);
|
||||
portfds[i].fd=0;
|
||||
portfds[i].events=0;
|
||||
if (verbose) printf("Read port fifo %s removed (%d).\n",portfilerd[i],portfdrd[i]);
|
||||
// TODO fd not valid anymore?
|
||||
}
|
||||
if (portfilewr[i]) {
|
||||
// close(portfdwr[i]);
|
||||
unlink(portfilewr[i]);
|
||||
if (verbose) printf("Write port fifo %s removed (%d).\n",portfilewr[i],portfdwr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int __dso_handle;
|
||||
|
||||
void sigterm_handler() {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int ioinit() {
|
||||
nportfds=0;
|
||||
for(int i=0;i<NUMPORTS;i++) {
|
||||
portfdrd[i]=portfdwr[i]=-1;
|
||||
portfilerd[i]=portfilewr[i]=NULL;
|
||||
}
|
||||
atexit(port_close);
|
||||
signal(SIGTERM, sigterm_handler);
|
||||
signal(SIGINT, sigterm_handler);
|
||||
}
|
||||
|
||||
/*
|
||||
Wait or check (nonblock=1) for IO and timeout events
|
||||
returns -1 if interrupted an nothing to run, else 0
|
||||
*/
|
||||
int iowait(context_t *C, int nonblock) {
|
||||
int status=0,timeout=0;
|
||||
context_t *c;
|
||||
reg_t *R;
|
||||
c=C;
|
||||
while (c) {
|
||||
// compute next pending timeout for contexts
|
||||
R=c->R;
|
||||
if (R->state&PTIMEOUT) {
|
||||
// delay
|
||||
int milliseconds=R->timeout-MILLIS();
|
||||
if (milliseconds<=0) {
|
||||
// timeout exhausted
|
||||
HandleTimeout(c);
|
||||
return 0;
|
||||
} else {
|
||||
if (!timeout) timeout = milliseconds;
|
||||
timeout=MIN(timeout,milliseconds);
|
||||
}
|
||||
}
|
||||
c=c->next;
|
||||
}
|
||||
#if DEBUG > 0
|
||||
log(LOGDEBUG2,"iowait timeout=%d ms nportfds=%d nonblock=%d\n",timeout,nportfds,nonblock);
|
||||
#endif
|
||||
|
||||
int n=poll(portfds,nportfds,timeout==0?(nonblock?0:-1):timeout);
|
||||
if (n<0 && errno==EINTR) {
|
||||
// Interrupt
|
||||
c=C;
|
||||
while (c) {
|
||||
R=c->R;
|
||||
if (RUNNABLE(R)) {
|
||||
#if DEBUG > 0
|
||||
log(LOGDEBUG2,"iowait (poll=%d) interrupted, returns %d (C#%d) (timeout=%d)\n",
|
||||
n,1,CONTEXTID(c),
|
||||
timeout);
|
||||
#endif
|
||||
return 1; // interrupted for event handling
|
||||
}
|
||||
c=c->next;
|
||||
}
|
||||
#if DEBUG > 0
|
||||
log(LOGDEBUG2,"iowait (poll=%d) interrupted, returns %d (no runnable C) (timeout=%d)\n",
|
||||
n,-1,
|
||||
timeout);
|
||||
#endif
|
||||
return -1; // interrupted by other reason
|
||||
}
|
||||
if (n>0) {
|
||||
// IO ready or HUP, handle IO, basically net messages
|
||||
status=0;
|
||||
#if HAS_NET > 0
|
||||
for(int i=0;i<NUMPORTS;i++) {
|
||||
if (portfds[i].revents&POLLHUP) {
|
||||
// hang up of other side, reopen port
|
||||
port_reopen(i,'r');
|
||||
continue;
|
||||
};
|
||||
#if HAS_NET_ROUTING > 0
|
||||
if (portfds[i].revents&POLLIN && Ports[i]->state==LSTART) {
|
||||
PortReceiver(Ports[i]);
|
||||
#else
|
||||
if (portfds[i].revents&POLLIN && Port->rxState==LSTART) {
|
||||
PortReceiver();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else if (timeout) {
|
||||
// Timeout
|
||||
c=C;
|
||||
status = 0;
|
||||
while (c) {
|
||||
status = status || HandleTimeout(c);
|
||||
c=c->next;
|
||||
}
|
||||
}
|
||||
#if DEBUG > 0
|
||||
log(LOGDEBUG2,"iowait (poll=%d) status=%d\n",n,status);
|
||||
#endif
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
Timer service control
|
||||
*/
|
||||
|
||||
|
||||
static timer_t timer1,timer2;
|
||||
static struct sigaction timer1_sigact,timer2_sigact;
|
||||
static struct sigevent timer1_sigevt,timer2_sigevt;
|
||||
|
||||
|
||||
|
||||
static void timer_signal_handler(int signum, siginfo_t *info, void *context)
|
||||
{
|
||||
int timernum=signum-SIGRTMIN;
|
||||
#if DEBUG>0
|
||||
log(LOGDEBUG2,"Timer[%d] interrupt, raising event\n",timernum);
|
||||
#endif
|
||||
RaiseEvent("timer",timernum);
|
||||
}
|
||||
|
||||
int timer_init() {
|
||||
/* setting signal and its handler */
|
||||
sigemptyset(&timer1_sigact.sa_mask);
|
||||
sigemptyset(&timer2_sigact.sa_mask);
|
||||
timer1_sigact.sa_sigaction = timer_signal_handler;
|
||||
timer2_sigact.sa_sigaction = timer_signal_handler;
|
||||
timer1_sigact.sa_flags = SA_SIGINFO;
|
||||
timer2_sigact.sa_flags = SA_SIGINFO;
|
||||
if (sigaction(SIGRTMIN+0, &timer1_sigact, NULL))
|
||||
return errno;
|
||||
if (sigaction(SIGRTMIN+1, &timer2_sigact, NULL))
|
||||
return errno;
|
||||
|
||||
/* setting timer event */
|
||||
timer1_sigevt.sigev_notify = SIGEV_SIGNAL;
|
||||
timer1_sigevt.sigev_signo = SIGRTMIN+0;
|
||||
timer1_sigevt.sigev_value.sival_ptr = NULL;
|
||||
timer2_sigevt.sigev_notify = SIGEV_SIGNAL;
|
||||
timer2_sigevt.sigev_signo = SIGRTMIN+1;
|
||||
timer2_sigevt.sigev_value.sival_ptr = NULL;
|
||||
|
||||
if (timer_create(CLOCK_REALTIME, &timer1_sigevt, &timer1))
|
||||
return errno;
|
||||
if (timer_create(CLOCK_REALTIME, &timer2_sigevt, &timer2))
|
||||
return errno;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_start(int index, int millis) {
|
||||
/* setting alarm time */
|
||||
#if DEBUG>0
|
||||
log(LOGDEBUG2,"Timer[%d] start %d ms\n",index,millis);
|
||||
#endif
|
||||
struct itimerspec alarm;
|
||||
alarm.it_value.tv_sec = millis/1000;
|
||||
alarm.it_value.tv_nsec = (millis-alarm.it_value.tv_sec*1000)*1000000L;
|
||||
alarm.it_interval.tv_sec = alarm.it_value.tv_sec;
|
||||
alarm.it_interval.tv_nsec = alarm.it_value.tv_nsec;
|
||||
|
||||
/* starting timer */
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (timer_settime(timer1, 0, &alarm, NULL))
|
||||
return errno;
|
||||
break;
|
||||
case 1:
|
||||
if (timer_settime(timer2, 0, &alarm, NULL))
|
||||
return errno;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int timer_stop(int index) {
|
||||
/* setting alarm time */
|
||||
#if DEBUG>0
|
||||
log(LOGDEBUG2,"Timer[%d] stop\n",index);
|
||||
#endif
|
||||
struct itimerspec alarm;
|
||||
alarm.it_value.tv_sec = 0L;
|
||||
alarm.it_value.tv_nsec = 0L;
|
||||
alarm.it_interval.tv_sec = 0L;
|
||||
alarm.it_interval.tv_nsec = 0L;
|
||||
|
||||
/* starting timer */
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (timer_settime(timer1, 0, &alarm, NULL))
|
||||
return errno;
|
||||
break;
|
||||
case 1:
|
||||
if (timer_settime(timer2, 0, &alarm, NULL))
|
||||
return errno;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void interrupt() {
|
||||
|
||||
}
|
||||
|
||||
char* readFile(char *path) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET); /* same as rewind(f); */
|
||||
char *string = malloc(fsize + 1);
|
||||
fread(string, fsize, 1, f);
|
||||
fclose(f);
|
||||
string[fsize] = 0;
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
// always directed to stdout
|
||||
void vLog(const char* fmtStr, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmtStr);
|
||||
vfprintf(stdout, fmtStr, va);
|
||||
fflush(stdout);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user