static gboolean aio_ctx_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { AioContext *ctx = (AioContext *) source; assert(callback == NULL); aio_dispatch(ctx); return true; }
int pass_on(ProgramState *s) { SIGSELECT sigsel[] = {0,FM_READ_PTR_REPLY}; union SIGNAL *sig; char child_read_buff[BUFSIZ], pipe_read_buff[BUFSIZ]; struct aiocb child_read_req, pipe_read_req; int rfd, wfd = 0; FmHandle rfh, child_rfh; int outstanding_writes = 0, got_some = 0, child_done = 0; if ((rfd = sf_open(s->r_pipe, O_RDONLY, 0)) < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.\n", s->r_pipe); rfd = 0; return 1; } attach(NULL,s->progpid); /* Open the log file */ erts_run_erl_log_open(); efs_examine_fd(rfd,FLIB_FD_HANDLE,&rfh); efs_examine_fd(s->ifd,FLIB_FD_HANDLE,&child_rfh); READ_AIO(child_read_req,s->ifd,BUFSIZ,child_read_buff); READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); while (1) { time_t now,last_activity; time(&last_activity); sig = receive_w_tmo(erts_run_erl_log_alive_minutes()*60000,sigsel); time(&now); if (sig) { erts_run_erl_log_activity(0,now,last_activity); } else { /* timeout */ erts_run_erl_log_activity(1,now,last_activity); continue; } switch (sig->signo) { case OS_ATTACH_SIG: { if (rfd) { sf_close(rfd); rfd = 0; } free_buf(&sig); child_done = 1; /* Make sure to to let all outstanding write request finish */ if (outstanding_writes) break; if (wfd) sf_close(wfd); return 0; } case FM_WRITE_PTR_REPLY: { if (sig->fm_write_ptr.status == EFS_SUCCESS) { if (sig->fm_write_ptr.actual < sig->fm_write_ptr.requested) { WRITE_AIO(wfd, sig->fm_write_ptr.requested-sig->fm_write_ptr.actual, sig->fm_write_ptr.buffer+sig->fm_write_ptr.actual); } } else { /* Assume to_erl has terminated. */ sf_close(wfd); wfd = 0; } free((char*)sig->fm_write_ptr.buffer); aio_dispatch(sig); if ((--outstanding_writes == 0) && child_done) { if (wfd) sf_close(wfd); return 0; } break; } case FM_READ_PTR_REPLY: { /* Child fd */ if (sig->fm_read_ptr.handle == child_rfh) { /* Child terminated */ if (sig->fm_read_ptr.status != EFS_SUCCESS || sig->fm_read_ptr.actual == 0) { if (rfd) { sf_close(rfd); rfd = 0; } if (sig->fm_read_ptr.status != EFS_SUCCESS) { ERROR0(LOG_ERR,"Erlang closed the connection."); aio_dispatch(sig); return 1; } /* child closed connection gracefully */ aio_dispatch(sig); if (outstanding_writes) { child_done = 1; break; } if (wfd) sf_close(wfd); return 0; } else { erts_run_erl_log_write(sig->fm_read_ptr.buffer, sig->fm_read_ptr.actual); if (wfd) { WRITE_AIO(wfd, sig->fm_read_ptr.actual, sig->fm_read_ptr.buffer); outstanding_writes++; } aio_dispatch(sig); READ_AIO(child_read_req, s->ifd,BUFSIZ, child_read_buff); } /* pipe fd */ } else if (sig->fm_read_ptr.handle == rfh) { if (sig->fm_read_ptr.status != EFS_SUCCESS) { if(rfd) sf_close(rfd); if(wfd) sf_close(wfd); aio_dispatch(sig); ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO."); return 1; } if (sig->fm_read_ptr.actual == 0) { /* to_erl closed its end of the pipe */ aio_dispatch(sig); sf_close(rfd); rfd = sf_open(s->r_pipe,O_RDONLY|DONT_BLOCK_PLEASE, 0); if (rfd < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", s->r_pipe); rfd = 0; } else { READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); } got_some = 0; /* reset for next session */ } else { int len = sig->fm_read_ptr.actual; char *buffer = sig->fm_read_ptr.buffer; if (!wfd) { /* Try to open the write pipe to to_erl. Now that we got some data * from to_erl, to_erl should already be reading this pipe - open * should succeed. But in case of error, we just ignore it. */ if ((wfd = sf_open(s->w_pipe, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { erts_run_erl_log_status("Client expected on FIFO %s, " "but can't open (len=%d)\n", s->w_pipe, sig->fm_read_ptr.actual); sf_close(rfd); rfd = sf_open(s->r_pipe, O_RDONLY|DONT_BLOCK_PLEASE, 0); if (rfd < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", s->r_pipe); return 1; } wfd = 0; } else { #ifdef DEBUG erts_run_erl_log_status("run_erl: %s opened for writing\n", s->w_pipe); #endif } } if (!got_some && wfd && buffer[0] == '\014') { char wbuf[30]; int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n", RUN_ERL_HI_VER, RUN_ERL_LO_VER); /* For some reason this, the first write aio seems to not get an FM_WRITE_PTR_REPLY, so we do not do: outstanding_writes++; */ WRITE_AIO(wfd, wlen, wbuf); } got_some = 1; /* Write the message */ #ifdef DEBUG erts_run_erl_log_status("Pty master write; "); #endif len = erts_run_erl_extract_ctrl_seq(buffer,len, s->ofd); if (len > 0) { int wlen = erts_run_erl_write_all(s->ofd, buffer, len); if (wlen != len) { aio_dispatch(sig); ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); if(rfd) sf_close(rfd); if(wfd) sf_close(wfd); return 1; } } #ifdef DEBUG erts_run_erl_log_status("OK\n"); #endif aio_dispatch(sig); READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_read_buff); } } break; } default: { free_buf(&sig); break; } } } }
int to_erl(int argc, char **argv) { char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX]; int i, len, wfd, rfd; char pipename[FILENAME_MAX]; int pipeIx = 1; int force_lock = 0; int got_some = 0; #ifdef __OSE__ struct aiocb stdin_read_req, pipe_read_req; FmHandle stdin_fh, pipe_fh; char *stdin_buf, *pipe_buf; char *buf; union SIGNAL *sig; #else /* __UNIX__ */ char buf[BUFSIZ]; fd_set readfds; #endif if (argc >= 2 && argv[1][0]=='-') { switch (argv[1][1]) { case 'h': usage(argv[0]); exit(1); case 'F': force_lock = 1; break; default: fprintf(stderr,"Invalid option '%s'\n",argv[1]); exit(1); } pipeIx = 2; } #ifdef DEBUG fprintf(stderr, "%s: pid is : %d\n", argv[0],(int) #ifdef __OSE__ current_process() #else /* __UNIX__ */ getpid() #endif ); #endif strn_cpy(pipename, sizeof(pipename), (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR)); if(*pipename && pipename[strlen(pipename)-1] == '/') { /* The user wishes us to find a pipe name in the specified */ /* directory */ int highest_pipe_num = 0; DIR *dirp; struct dirent *direntp; dirp = opendir(pipename); if(!dirp) { fprintf(stderr, "Can't access pipe directory %s: %s\n", pipename, strerror(errno)); exit(1); } /* Check the directory for existing pipes */ while((direntp=readdir(dirp)) != NULL) { if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) { int num = atoi(direntp->d_name+PIPE_STUBLEN+1); if(num > highest_pipe_num) highest_pipe_num = num; } } closedir(dirp); strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"), PIPE_STUBNAME, highest_pipe_num); } /* if */ /* read FIFO */ sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename); /* write FIFO */ sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename); #ifndef __OSE__ /* Check that nobody is running to_erl on this pipe already */ if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) { /* Open as server succeeded -- to_erl is already running! */ close(wfd); fprintf(stderr, "Another to_erl process already attached to pipe " "%s.\n", pipename); if (force_lock) { fprintf(stderr, "But we proceed anyway by force (-F).\n"); } else { exit(1); } } #endif if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { #ifdef DEBUG fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1); #endif fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); exit(1); } #ifdef DEBUG fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1); #endif if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { #ifdef DEBUG fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2); #endif fprintf(stderr, "No running Erlang on pipe %s: %s\n", pipename, strerror(errno)); close(rfd); exit(1); } #ifdef DEBUG fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2); #endif #ifndef __OSE__ fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename); #else fprintf(stderr, "Attaching to %s (^C to exit)\n\n", pipename); #endif #ifndef __OSE__ /* Set break handler to our handler */ signal(SIGINT,handle_ctrlc); /* * Save the current state of the terminal, and set raw mode. */ if (tcgetattr(0, &tty_rmode) , 0) { fprintf(stderr, "Cannot get terminals current mode\n"); exit(-1); } tty_smode = tty_rmode; tty_eof = '\004'; /* Ctrl+D to exit */ #ifdef DEBUG show_terminal_settings(&tty_rmode); #endif tty_smode.c_iflag = 1*BRKINT |/*Signal interrupt on break.*/ 1*IGNPAR |/*Ignore characters with parity errors.*/ 1*ISTRIP |/*Strip character.*/ 0; #if 0 0*IGNBRK |/*Ignore break condition.*/ 0*PARMRK |/*Mark parity errors.*/ 0*INPCK |/*Enable input parity check.*/ 0*INLCR |/*Map NL to CR on input.*/ 0*IGNCR |/*Ignore CR.*/ 0*ICRNL |/*Map CR to NL on input.*/ 0*IUCLC |/*Map upper-case to lower-case on input.*/ 0*IXON |/*Enable start/stop output control.*/ 0*IXANY |/*Enable any character to restart output.*/ 0*IXOFF |/*Enable start/stop input control.*/ 0*IMAXBEL|/*Echo BEL on input line too long.*/ #endif tty_smode.c_oflag = 1*OPOST |/*Post-process output.*/ 1*ONLCR |/*Map NL to CR-NL on output.*/ #ifdef XTABS 1*XTABS |/*Expand tabs to spaces. (Linux)*/ #endif #ifdef OXTABS 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/ #endif #ifdef NL0 1*NL0 |/*Select newline delays*/ #endif #ifdef CR0 1*CR0 |/*Select carriage-return delays*/ #endif #ifdef TAB0 1*TAB0 |/*Select horizontal tab delays*/ #endif #ifdef BS0 1*BS0 |/*Select backspace delays*/ #endif #ifdef VT0 1*VT0 |/*Select vertical tab delays*/ #endif #ifdef FF0 1*FF0 |/*Select form feed delays*/ #endif 0; #if 0 0*OLCUC |/*Map lower case to upper on output.*/ 0*OCRNL |/*Map CR to NL on output.*/ 0*ONOCR |/*No CR output at column 0.*/ 0*ONLRET |/*NL performs CR function.*/ 0*OFILL |/*Use fill characters for delay.*/ 0*OFDEL |/*Fill is DEL, else NULL.*/ 0*NL1 | 0*CR1 | 0*CR2 | 0*CR3 | 0*TAB1 | 0*TAB2 | 0*TAB3 |/*Expand tabs to spaces.*/ 0*BS1 | 0*VT1 | 0*FF1 | #endif /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */ /* advisable if this is a *real* terminal, such as the console. In fact */ /* this may hang the entire machine, deep, deep down (signalling break */ /* or toggling the abort switch doesn't help) */ tty_smode.c_lflag = 0; #if 0 0*ISIG |/*Enable signals.*/ 0*ICANON |/*Canonical input (erase and kill processing).*/ 0*XCASE |/*Canonical upper/lower presentation.*/ 0*ECHO |/*Enable echo.*/ 0*ECHOE |/*Echo erase character as BS-SP-BS.*/ 0*ECHOK |/*Echo NL after kill character.*/ 0*ECHONL |/*Echo NL.*/ 0*NOFLSH |/*Disable flush after interrupt or quit.*/ 0*TOSTOP |/*Send SIGTTOU for background output.*/ 0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/ 0*ECHOPRT|/*Echo erase character as character erased.*/ 0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/ 0*FLUSHO |/*Output is being flushed.*/ 0*PENDIN |/*Retype pending input at next read or input character.*/ 0*IEXTEN |/*Enable extended (implementation-defined) functions.*/ #endif tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */ tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */ tty_smode.c_cc[VINTR] =3; tcsetattr(0, TCSADRAIN, &tty_smode); #ifdef DEBUG show_terminal_settings(&tty_smode); #endif #endif /* !__OSE__ */ /* * "Write a ^L to the FIFO which causes the other end to redisplay * the input line." * This does not seem to work as was intended in old comment above. * However, this control character is now (R12B-3) used by run_erl * to trigger the version handshaking between to_erl and run_erl * at the start of every new to_erl-session. */ if (write(wfd, "\014", 1) < 0) { fprintf(stderr, "Error in writing ^L to FIFO.\n"); } #ifdef __OSE__ /* we have a tiny stack so we malloc the buffers */ stdin_buf = malloc(sizeof(char) * BUFSIZ); pipe_buf = malloc(sizeof(char) * BUFSIZ); efs_examine_fd(rfd,FLIB_FD_HANDLE,&pipe_fh); efs_examine_fd(0,FLIB_FD_HANDLE,&stdin_fh); READ_AIO(stdin_read_req,0,BUFSIZ,stdin_buf); READ_AIO(pipe_read_req,rfd,BUFSIZ,pipe_buf); #endif /* * read and write */ while (1) { #ifndef __OSE__ FD_ZERO(&readfds); FD_SET(0, &readfds); FD_SET(rfd, &readfds); if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) { if (recv_sig) { FD_ZERO(&readfds); } else { fprintf(stderr, "Error in select.\n"); break; } } len = 0; /* * Read from terminal and write to FIFO */ if (recv_sig) { switch (recv_sig) { case SIGINT: fprintf(stderr, "[Break]\n\r"); buf[0] = '\003'; len = 1; break; case SIGWINCH: len = window_size_seq(buf,sizeof(buf)); break; default: fprintf(stderr,"Unexpected signal: %u\n",recv_sig); } recv_sig = 0; } else #else /* __OSE__ */ SIGSELECT sigsel[] = {0}; sig = receive(sigsel); len = 0; #endif #ifndef __OSE__ if (FD_ISSET(0,&readfds)) { len = read(0, buf, sizeof(buf)); #else /* __OSE__ */ if (sig->signo == FM_READ_PTR_REPLY && sig->fm_read_ptr.handle == stdin_fh) { len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1; buf = sig->fm_read_ptr.buffer; #endif if (len <= 0) { close(rfd); close(wfd); if (len < 0) { fprintf(stderr, "Error in reading from stdin.\n"); } else { fprintf(stderr, "[EOF]\n\r"); } break; } /* check if there is an eof character in input */ for (i = 0; i < len-1 && buf[i] != tty_eof; i++); if (buf[i] == tty_eof) { fprintf(stderr, "[Quit]\n\r"); break; } } if (len) { #ifdef DEBUG if(write(1, buf, len)); #endif if (write_all(wfd, buf, len) != len) { fprintf(stderr, "Error in writing to FIFO.\n"); close(rfd); close(wfd); break; } STATUS("\" OK\r\n"); #ifdef __OSE__ aio_dispatch(sig); READ_AIO(stdin_read_req, 0, BUFSIZ, stdin_buf); #endif } /* * Read from FIFO, write to terminal. */ #ifndef __OSE__ if (FD_ISSET(rfd, &readfds)) { STATUS("FIFO read: "); len = read(rfd, buf, BUFSIZ); #else /* __OSE__ */ if (sig->signo == FM_READ_PTR_REPLY && sig->fm_read_ptr.handle == pipe_fh) { len = sig->fm_read_ptr.status == EFS_SUCCESS ? sig->fm_read_ptr.actual : -1; buf = sig->fm_read_ptr.buffer; #endif if (len < 0 && errno == EAGAIN) { /* * No data this time, but the writing end of the FIFO is still open. * Do nothing. */ ; } else if (len <= 0) { /* * Either an error or end of file. In either case, break out * of the loop. */ close(rfd); close(wfd); if (len < 0) { fprintf(stderr, "Error in reading from FIFO.\n"); } else fprintf(stderr, "[End]\n\r"); break; } else { if (!got_some) { if ((len=version_handshake(buf,len,wfd)) < 0) { close(rfd); close(wfd); break; } #ifndef __OSE__ if (protocol_ver >= 1) { /* Tell run_erl size of terminal window */ signal(SIGWINCH, handle_sigwinch); raise(SIGWINCH); } #endif got_some = 1; } /* * We successfully read at least one character. Write what we got. */ STATUS("Terminal write: \""); if (write_all(1, buf, len) != len) { fprintf(stderr, "Error in writing to terminal.\n"); close(rfd); close(wfd); break; } STATUS("\" OK\r\n"); #ifdef __OSE__ aio_dispatch(sig); READ_AIO(pipe_read_req, rfd, BUFSIZ, pipe_buf); #endif } } } #ifndef __OSE__ /* * Reset terminal characterstics * XXX */ tcsetattr(0, TCSADRAIN, &tty_rmode); #endif return 0; } /* Call write() until entire buffer has been written or error. * Return len or -1. */ static int write_all(int fd, const char* buf, int len) { int left = len; int written; while (left) { written = write(fd,buf,left); if (written < 0) { return -1; } left -= written; buf += written; } return len; } #ifndef __OSE__ static int window_size_seq(char* buf, size_t bufsz) { #ifdef TIOCGWINSZ struct winsize ws; static const char prefix[] = "\033_"; static const char suffix[] = "\033\\"; /* This Esc sequence is called "Application Program Command" and seems suitable to use for our own customized stuff. */ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) { int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s", prefix, ws.ws_col, ws.ws_row, suffix); return len; } #endif /* TIOCGWINSZ */ return 0; } #endif /* !__OSE__ */ /* to_erl run_erl * | | * |---------- '\014' -------->| (session start) * | | * |<---- "[run_erl v1-0]" ----| (version interval) * | | * |--- Esc_"version=1"Esc\ -->| (common version) * | | */ static int version_handshake(char* buf, int len, int wfd) { unsigned re_high=0, re_low; char *end = find_str(buf,len,"]\n"); if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) { char wbuf[30]; int wlen; if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) { fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n", RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low); return -1; } /* Choose highest common version */ protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER; wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\", protocol_ver); if (write_all(wfd, wbuf, wlen) < 0) { fprintf(stderr,"Failed to send version handshake\n"); return -1; } end += 2; len -= (end-buf); memmove(buf,end,len); } else { /* we assume old run_erl without version handshake */ protocol_ver = 0; } if (re_high != RUN_ERL_HI_VER) { fprintf(stderr,"run_erl has different version, " "using common protocol level %u\n", protocol_ver); } return len; }