Mon 16 Mar 11:09:06 CET 2026

This commit is contained in:
sbosse 2026-03-16 11:12:02 +01:00
parent e9a642eb1b
commit 3725ba9c13

477
src/unix.c Normal file
View 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);
}