/* pass_on() * Is the work loop of the logger. Selects on the pipe to the to_erl * program erlang. If input arrives from to_erl it is passed on to * erlang. */ static void pass_on(pid_t childpid) { int len; fd_set readfds; fd_set writefds; fd_set* writefds_ptr; struct timeval timeout; time_t last_activity; char buf[BUFSIZ]; char log_alive_buffer[ALIVE_BUFFSIZ+1]; int lognum; int rfd, wfd=0, lfd=0; int maxfd; int ready; int got_some = 0; /* from to_erl */ /* Open the to_erl pipe for reading. * We can't open the writing side because nobody is reading and * we'd either hang or get an error. */ if ((rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); exit(1); } #ifdef DEBUG status("run_erl: %s opened for reading\n", fifo2); #endif /* Open the log file */ lognum = find_next_log_num(); lfd = open_log(lognum, O_RDWR|O_APPEND|O_CREAT|O_SYNC); /* Enter the work loop */ while (1) { int exit_status; maxfd = MAX(rfd, mfd); maxfd = MAX(wfd, maxfd); FD_ZERO(&readfds); FD_SET(rfd, &readfds); FD_SET(mfd, &readfds); FD_ZERO(&writefds); if (outbuf_size() == 0) { writefds_ptr = NULL; } else { FD_SET(wfd, &writefds); writefds_ptr = &writefds; } time(&last_activity); timeout.tv_sec = log_alive_minutes*60; /* don't assume old BSD bug */ timeout.tv_usec = 0; ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout); if (ready < 0) { if (errno == EINTR) { if (waitpid(childpid, &exit_status, WNOHANG) == childpid) { /* * The Erlang emulator has terminated. Give us some more * time to write out any pending data before we terminate too. */ alarm(5); } FD_ZERO(&readfds); FD_ZERO(&writefds); } else { /* Some error occured */ ERRNO_ERR0(LOG_ERR,"Error in select."); exit(1); } } else { time_t now; if (waitpid(childpid, &exit_status, WNOHANG) == childpid) { alarm(5); FD_ZERO(&readfds); FD_ZERO(&writefds); } /* Check how long time we've been inactive */ time(&now); if(!ready || now - last_activity > log_activity_minutes*60) { /* Either a time out: 15 minutes without action, */ /* or something is coming in right now, but it's a long time */ /* since last time, so let's write a time stamp this message */ struct tm *tmptr; if (log_alive_in_gmt) { tmptr = gmtime(&now); } else { tmptr = localtime(&now); } if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format, tmptr)) { strn_cpy(log_alive_buffer, sizeof(log_alive_buffer), "(could not format time in 256 positions " "with current format string.)"); } log_alive_buffer[ALIVE_BUFFSIZ] = '\0'; sn_printf(buf, sizeof(buf), "\n===== %s%s\n", ready?"":"ALIVE ", log_alive_buffer); write_to_log(&lfd, &lognum, buf, strlen(buf)); } } /* * Write any pending output first. */ if (FD_ISSET(wfd, &writefds)) { int written; char* buf = outbuf_first(); len = outbuf_size(); written = sf_write(wfd, buf, len); if (written < 0 && errno == EAGAIN) { /* * Nothing was written - this is really strange because * select() told us we could write. Ignore. */ } else if (written < 0) { /* * A write error. Assume that to_erl has terminated. */ clear_outbuf(); sf_close(wfd); wfd = 0; } else { /* Delete the written part (or all) from the buffer. */ outbuf_delete(written); } } /* * Read master pty and write to FIFO. */ if (FD_ISSET(mfd, &readfds)) { #ifdef DEBUG status("Pty master read; "); #endif if ((len = sf_read(mfd, buf, BUFSIZ)) <= 0) { sf_close(rfd); if(wfd) sf_close(wfd); sf_close(mfd); unlink(fifo1); unlink(fifo2); if (len < 0) { if(errno == EIO) ERROR0(LOG_ERR,"Erlang closed the connection."); else ERRNO_ERR0(LOG_ERR,"Error in reading from terminal"); exit(1); } exit(0); } write_to_log(&lfd, &lognum, buf, len); /* * Save in the output queue. */ if (wfd) { outbuf_append(buf, len); } } /* * Read from FIFO, write to master pty */ if (FD_ISSET(rfd, &readfds)) { #ifdef DEBUG status("FIFO read; "); #endif if ((len = sf_read(rfd, buf, BUFSIZ)) < 0) { sf_close(rfd); if(wfd) sf_close(wfd); sf_close(mfd); unlink(fifo1); unlink(fifo2); ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO."); exit(1); } if(!len) { /* to_erl closed its end of the pipe */ sf_close(rfd); rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); if (rfd < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); exit(1); } got_some = 0; /* reset for next session */ } else { 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(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) { status("Client expected on FIFO %s, but can't open (len=%d)\n", fifo1, len); sf_close(rfd); rfd = sf_open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0); if (rfd < 0) { ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2); exit(1); } wfd = 0; } else { #ifdef DEBUG status("run_erl: %s opened for writing\n", fifo1); #endif } } if (!got_some && wfd && buf[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); outbuf_append(wbuf,wlen); } got_some = 1; /* Write the message */ #ifdef DEBUG status("Pty master write; "); #endif len = extract_ctrl_seq(buf, len); if(len==1 && buf[0] == '\003') { kill(childpid,SIGINT); } else if (len>0 && write_all(mfd, buf, len) != len) { ERRNO_ERR0(LOG_ERR,"Error in writing to terminal."); sf_close(rfd); if(wfd) sf_close(wfd); sf_close(mfd); exit(1); } } #ifdef DEBUG status("OK\n"); #endif } } } /* pass_on() */
char* wordwrap(char* inbuf, int len, int oldlen, BOOL handle_quotes) { int l; int crcount=0; long i,k,t; int ocol=1; int icol=1; char* outbuf; char* outp; char* linebuf; char* prefix=NULL; int prefix_len=0; int prefix_bytes=0; int quote_count=0; int old_prefix_bytes=0; int outbuf_size=0; int inbuf_len=strlen(inbuf); outbuf_size=inbuf_len*3+1; if((outbuf=(char*)malloc(outbuf_size))==NULL) return NULL; outp=outbuf; if((linebuf=(char*)malloc(inbuf_len+2))==NULL) { /* room for ^A codes */ free(outbuf); return NULL; } if(handle_quotes) { if((prefix=(char *)malloc(inbuf_len+1))==NULL) { /* room for ^A codes */ free(linebuf); free(outbuf); return NULL; } prefix[0]=0; } outbuf[0]=0; /* Get prefix from the first line (ouch) */ l=0; i=0; if(handle_quotes && (quote_count=get_prefix(inbuf, &prefix_bytes, &prefix_len, len*2+2))!=0) { i+=prefix_bytes; if(prefix_len>len/3*2) { /* This prefix is insane (more than 2/3rds of the new width) hack it down to size */ /* Since we're hacking it, we will always end up with a hardcr on this line. */ /* ToDo: Something prettier would be nice. */ sprintf(prefix," %d> ",quote_count); prefix_len=strlen(prefix); prefix_bytes=strlen(prefix); } else { memcpy(prefix,inbuf,prefix_bytes); /* Terminate prefix */ prefix[prefix_bytes]=0; } memcpy(linebuf,prefix,prefix_bytes); l=prefix_bytes; ocol=prefix_len+1; icol=prefix_len+1; old_prefix_bytes=prefix_bytes; } for(; inbuf[i]; i++) { if(oldlen == 0) icol=-256; if(l>=len*2+2) { l-=4; linebuf[l]=0; // lprintf(LOG_CRIT, "Word wrap line buffer exceeded... munging line %s",linebuf); } switch(inbuf[i]) { case '\r': crcount++; break; case '\n': if(handle_quotes && (quote_count=get_prefix(inbuf+i+1, &prefix_bytes, &prefix_len, len*2+2))!=0) { /* Move the input pointer offset to the last char of the prefix */ i+=prefix_bytes; } if(!inbuf[i+1]) { /* EOF */ linebuf[l++]='\r'; linebuf[l++]='\n'; outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); l=0; ocol=1; } /* If there's a new prefix, it is a hardcr */ else if(compare_prefix(prefix, old_prefix_bytes, inbuf+i+1-prefix_bytes, prefix_bytes)!=0) { if(prefix_len>len/3*2) { /* This prefix is insane (more than 2/3rds of the new width) hack it down to size */ /* Since we're hacking it, we will always end up with a hardcr on this line. */ /* ToDo: Something prettier would be nice. */ sprintf(prefix," %d> ",quote_count); prefix_len=strlen(prefix); prefix_bytes=strlen(prefix); } else { memcpy(prefix,inbuf+i+1-prefix_bytes,prefix_bytes); /* Terminate prefix */ prefix[prefix_bytes]=0; } linebuf[l++]='\r'; linebuf[l++]='\n'; outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); memcpy(linebuf,prefix,prefix_bytes); l=prefix_bytes; ocol=prefix_len+1; old_prefix_bytes=prefix_bytes; } else if(isspace((unsigned char)inbuf[i+1])) { /* Next line starts with whitespace. This is a "hard" CR. */ linebuf[l++]='\r'; linebuf[l++]='\n'; outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); l=prefix_bytes; ocol=prefix_len+1; } else { if(icol < oldlen) { /* If this line is overly long, It's impossible for the next word to fit */ /* k will equal the length of the first word on the next line */ for(k=0; inbuf[i+1+k] && (!isspace((unsigned char)inbuf[i+1+k])); k++); if(icol+k+1 < oldlen) { /* The next word would have fit but isn't here. Must be a hard CR */ linebuf[l++]='\r'; linebuf[l++]='\n'; outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); if(prefix) memcpy(linebuf,prefix,prefix_bytes); l=prefix_bytes; ocol=prefix_len+1; } else { /* Not a hard CR... add space if needed */ if(l<1 || !isspace((unsigned char)linebuf[l-1])) { linebuf[l++]=' '; ocol++; } } } else { /* Not a hard CR... add space if needed */ if(l<1 || !isspace((unsigned char)linebuf[l-1])) { linebuf[l++]=' '; ocol++; } } } icol=prefix_len+1; break; case '\x1f': /* Delete... meaningless... strip. */ break; case '\b': /* Backspace... handle if possible, but don't go crazy. */ if(l>0) { if(l>1 && linebuf[l-2]=='\x01') { if(linebuf[l-1]=='\x01') { ocol--; icol--; } l-=2; } else { l--; ocol--; icol--; } } break; case '\t': /* TAB */ linebuf[l++]=inbuf[i]; /* Can't ever wrap on whitespace remember. */ icol++; ocol++; while(ocol%8) ocol++; while(icol%8) icol++; break; case '\x01': /* CTRL-A */ linebuf[l++]=inbuf[i++]; if(inbuf[i]!='\x01') { linebuf[l++]=inbuf[i]; break; } default: linebuf[l++]=inbuf[i]; ocol++; icol++; if(ocol>len && !isspace((unsigned char)inbuf[i])) { /* Need to wrap here */ /* Find the start of the last word */ k=l; /* Original next char */ l--; /* Move back to the last char */ while((!isspace((unsigned char)linebuf[l])) && l>0) /* Move back to the last non-space char */ l--; if(l==0) { /* Couldn't wrap... must chop. */ l=k; while(l>1 && linebuf[l-2]=='\x01' && linebuf[l-1]!='\x01') l-=2; if(l>0 && linebuf[l-1]=='\x01') l--; if(l>0) l--; } t=l+1; /* Store start position of next line */ /* Move to start of whitespace */ while(l>0 && isspace((unsigned char)linebuf[l])) l--; outbuf_append(&outbuf, &outp, linebuf, l+1, &outbuf_size); outbuf_append(&outbuf, &outp, "\r\n", 2, &outbuf_size); /* Move trailing words to start of buffer. */ l=prefix_bytes; if(k-t>0) /* k-1 is the last char position. t is the start of the next line position */ memmove(linebuf+l, linebuf+t, k-t); l+=k-t; /* Find new ocol */ for(ocol=prefix_len+1,t=prefix_bytes; t<l; t++) { switch(linebuf[t]) { case '\x01': /* CTRL-A */ t++; if(linebuf[t]!='\x01') break; /* Fall-through */ default: ocol++; } } } } } /* Trailing bits. */ if(l) { linebuf[l++]='\r'; linebuf[l++]='\n'; outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size); } *outp=0; /* If there were no CRs in the input, strip all CRs */ if(!crcount) { for(inbuf=outbuf; *inbuf; inbuf++) { if(*inbuf=='\r') memmove(inbuf, inbuf+1, strlen(inbuf)); } } free(linebuf); if(prefix) free(prefix); return outbuf; }