This is link.c in view mode; [Download] [Up]
#define I_SYS #define I_SOCKET #define I_ERRNO #define I_IOCTL #define I_STRING #include "includes.h" #include "debug.h" #include <sys/stat.h> #include <arpa/inet.h> #ifndef S_ISREG #define S_ISREG(a) (((a) & S_IFMT) == S_IFREG) #define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR) #endif #ifdef X_DEBUG static int x_debug = 0; #endif extern do_stats(un_char *, int, struct Client *); /* * This modules handles multiplexing the clients onto the serial stream. * * do_link_in() is called when there are in packets waiting, and * do_link_out() is called when there is something in the link_out buffer and * the serial out buffer is empty. */ /*-----------------------------------------------------------------------*/ /* Local function protypes */ int get_data(un_char *, int); void put_data(un_char *, int); int get_client_data(struct Client *); void put_client_data(struct Client *, int); /*-----------------------------------------------------------------------*/ /* Data */ int curr_in_stream = -1, curr_out_stream = -1 ; /* we have this in the open to peek to see if compression is * wanted... croutons */ static struct Client *curr_client = 0; int new_packet = 0; /*-----------------------------------------------------------------------*/ void do_link_out(void) { int len; /* Add another packet to the out packet list */ if (p_out_num >= window_size) { /* naff off. The packet window is full. */ /* This is actually normal, so don't */ /* print a message. */ #if 0 DEBUG_LINK(stderr, "Tried to do link_out with p_out_num == %d\n", p_out_num); #endif return; } /* put some data in the packet. Get up to 'max' bytes. */ /* returns the length. If the length is -ve then it has been compressed */ new_packet = 1; len = get_data(p_out[p_out_s].data, out_mask - 2); if (!len) { /* All the data waiting was control data for local daemon */ /* We can handle this. */ return; } p_out[p_out_s].timeout = 0; /* Transmit it right away.. */ p_out[p_out_s].trans = 0; p_out[p_out_s].len = len > 0 ? len : -len; p_out[p_out_s].type = (len > 0) ? (seven_bit_out ? 2 : 0) : 1; DEBUG_LINK(stderr, "%s:Added pack %d to out Q\n", term_server, p_out_s); p_out_s = (p_out_s + 1) & 31; p_out_num ++; } void do_link_in(void) { /* Takes packet of the in packet list and feeds it to clients. */ static un_char uncomp_buff[2049]; int l; while (p_in[p_in_e].type >= 0) { /* feed data out */ DEBUG_LINK(stderr, "%s: Handleing p %d off in Q\n", term_server, p_in_e); if (p_in[p_in_e].type == 1) { extern int stat_uncomp_in, stat_uncomp_out; l =uncompress(p_in[p_in_e].data , p_in[p_in_e].len, uncomp_buff); stat_uncomp_in += p_in[p_in_e].len; stat_uncomp_out += l; put_data(uncomp_buff, l); } else if (p_in[p_in_e].type == 0) { put_data(p_in[p_in_e].data, p_in[p_in_e].len); } else { l = s_2_e_buff(p_in[p_in_e].data, uncomp_buff, p_in[p_in_e].len); put_data(uncomp_buff, l); } p_in[p_in_e].type = -1; p_in_e = (p_in_e + 1) & 31; p_in_num --; } } /*---------------------------------------------------------------------------*/ /* This is where compression will eventually get done */ /* For now , we just get some bytes */ /* we are compressing now. */ int get_data(un_char *b, int len) { extern int tok_byte_width_out; int i, j, k; /* this is a horrible kludge, but without major reorignization, * this is the simplest way. we need to know if the current * packet for the current client should be compressed, so we * need to know the client. we get the first byte (which forces * which client we are getting data from) and then peek and * see what that client wants. we then pass along the byte * that we read.. (this is the REAL kludge). * croutons. */ /* Get a byte, so we we have a client. */ k = get_client_byte(); if ( k < 0 ) /* no data */ return 0; /* Now that we have a client, we can */ /* check to see wether we want to */ /* compress the data or not. */ if (curr_client->compress && (j = compress(b, len-1, k)) > 0) { j = ( j + tok_byte_width_out - 1) / tok_byte_width_out; return -j; /* If we only have a seven bit output */ /* line, then we want to pack 8 bytes */ /* to 7 seven bit bytes. */ } else if (seven_bit_out) { len = (len * 7) / 8; b[0] = 0; j = e_2_s_put(b, k, 0); while ((j>>3) < len) { if ((i = get_client_byte()) < 0) break; j = e_2_s_put(b, i, j); } return (j>>3) + 1; } else { /* Else, just dump the data, we can */ /* handle it. */ b[0] = k; /* put the first byte in the array */ j = 1; while (j < len) { i = get_client_byte(); if (i < 0) break; b[j ++ ] = i; } } return j; } /*---------------------------------------------------------------------------*/ void ADD_BUFF(struct Client *clt, int c) { add_to_buffer( &((clt)->out_buff), c); } void ADD_IN_BUFF(struct Client *clt, int c) { add_to_buffer( &((clt)->in_buff), c); } void clear_buffers(struct Client *cl) { cl->in_buff.size = cl->in_buff.start = cl->in_buff.end = 0; cl->out_buff.size = cl->out_buff.start = cl->out_buff.end = 0; add_to_buffer (& (cl->in_buff), 0); get_from_buffer (& (cl->in_buff ) ); add_to_buffer (& (cl->out_buff), 0); get_from_buffer (& (cl->out_buff ) ); } void add_ret_buff(struct Client *cl, int which, int byte) { if (!which) ADD_IN_BUFF(cl, byte); else put_client_data(cl, byte); } void add_ret_buff_str(struct Client *cl, int which, char *s) { int i; for (i = 0; s[i];++i) add_ret_buff(cl, which, s[i]); } void ret_fail(struct Client *cl, int which, int fatal, char *p) { add_ret_buff(cl, which, SWITCH); add_ret_buff(cl, which, SWITCH-3); add_ret_buff(cl, which, I_FAIL); if (p) { add_ret_buff_str(cl, which, p); add_ret_buff_str(cl, which, ": "); } add_ret_buff_str(cl, which, strerror(errno)); add_ret_buff(cl, which, 0); if(!fatal) return; add_ret_buff(cl, which, SWITCH); add_ret_buff(cl, which, SWITCH-2); add_ret_buff(cl, which, C_CLOSE); add_ret_buff(cl, which, 0); } void ret_ok(struct Client *cl, int which) { add_ret_buff(cl, which, SWITCH); add_ret_buff(cl, which, SWITCH-3); add_ret_buff(cl, which, I_OK); add_ret_buff(cl, which, 0); } void do_control(int local , struct Client *cl, un_char *c) { int i; #ifdef SYSV struct utsname unam; #endif DEBUG_FP(stderr, "%s:do_control on client %d:%s:\n", term_server, cl->number, c); switch(c[0]) { case C_NAME: DEBUG_FP(stderr, "%s:C_NAME\n", term_server); sprintf(cl->name, "%s", c+1); ret_ok(cl, local); break; case C_CLOSE: DEBUG_FP(stderr, "%s:C_CLOSE\n", term_server); if (cl->state == 1) cl->state = 3; /* Go to flush buffers and close. */ break; case C_CLCLOSE: DEBUG_FP(stderr, "%s:C_CLCLOSE\n", term_server); if (cl->state == 1) cl->state = 4; ret_ok(cl, local); break; case C_DUMB: DEBUG_FP(stderr, "%s:C_DUMB\n", term_server); cl->type &= ~T_SMART; cl->dump_count = 0; break; case C_DUMP: DEBUG_FP(stderr, "%s: C_DUMP %d\n", term_server, atoi((char *) (c+1))); ret_ok(cl, local); cl->type &= ~T_SMART; cl->dump_count = atoi((char *) (c+1))+1; break; case C_OPEN: if (cl->fd >=0) close(cl->fd); DEBUG_FP(stderr,"%s:got C_OPEN\n", term_server); cl->fd = open((char *) (c + 1), O_RDWR | O_CREAT, 0600); DEBUG_FP(stderr, "%s:name was %s\n", term_server, c + 1); if (cl->fd < 0) { DEBUG_FP(stderr, "%s: open failed\n",term_server); ret_fail(cl,local,1, "open() failed"); break; } cl->type= T_WRFILE; cl->cl_type = CL_FILE; cl->state = 1; ret_ok(cl,local); break; case C_UPLOAD: if (cl->fd>=0) close(cl->fd); DEBUG_FP(stderr,"%s:got C_UPLOAD\n", term_server); cl->fd = open((char *) (c + 1), O_WRONLY | O_CREAT | O_TRUNC, 0600 ); DEBUG_FP(stderr, "%s:name was %s\n", term_server, c + 1); if (cl->fd < 0) { DEBUG_FP(stderr, "%s: open failed\n",term_server); ret_fail(cl, local, 1, "open() failed"); break; } cl->type = T_WRFILE; cl->cl_type = CL_FILE; cl->state = 1; ret_ok(cl, local); break; case C_DOWNLOAD: if (cl->fd>=0) close(cl->fd); DEBUG_FP(stderr,"%s:got C_DOWNLOAD\n", term_server); cl->fd = open((char *) (c + 1), O_RDONLY ); DEBUG_FP(stderr, "%s:name was %s\n", term_server, c + 1); if (cl->fd < 0) { DEBUG_FP(stderr, "%s: open failed\n",term_server); ret_fail(cl,local, 1, "open() failed"); break; } cl->type = T_RDFILE; cl->cl_type = CL_FILE; cl->state = 1; ret_ok(cl,local); break; case C_PTYEXEC: case C_EXEC: if (cl->fd>=0) close(cl->fd); DEBUG_FP(stderr, "%s: %s on client %d (%s) \n", term_server, c[0]==C_PTYEXEC?"C_PTYEXEC":"C_EXEC", cl->number, c+1); if (denyrsh) cl->fd = -5; else { if (c[0] == C_PTYEXEC) cl->fd = open_pty((char *)(c + 1)); else cl->fd = open_socket((char *)(c + 1)); } if (cl->fd < 0) { char *p; DEBUG_FP(stderr, "%s: failed to open client: error: %d\n", term_server, cl->fd); switch (cl->fd) { case -1: p = "Couldn't get pty"; break; case -2: p = "fchmod() failed"; break; case -3: p = "fork() failed"; break; case -4: p = "socketpair() failed"; break; case -5: p = "Permission denied"; break; default: p = "Unknown failure"; break; } ret_fail(cl, local, 1, p); break; } DEBUG_FP(stderr, "%s: opened client\n", term_server); cl->type = T_WRFILE | T_RDFILE; cl->cl_type = CL_CHILD; cl->state = 1; cl->pid = pty_pid; DEBUG_FP(stderr, "%s: Got pid %d\n", term_server, pty_pid); ret_ok(cl, local); break; case C_BIND: DEBUG_FP(stderr, "%s: C_BIND %s\n", term_server, c +1); { int port; int s; port = atoi((char *) (c+1)); s = bind_tcp(port); if (s < 0) { ret_fail(cl, local , 1, "bind_tcp() failed"); DEBUG_FP(stderr, "%s:Bind_tcp failed (%d)\n", term_server, port); } set_nonblock(s); if (cl->fd>=0) close(cl->fd); cl->fd = s; cl->cl_type = CL_BOUND; cl->type = T_RDFILE | T_WRFILE; cl->state = 1; ret_ok(cl, local); } break; case C_ACCEPT: { #ifdef NO_UNIX_DOMAIN struct sockaddr_in dummy; #else struct sockaddr_un dummy; #endif int din = sizeof(dummy); int s; cl->type |= T_RDFILE; DEBUG_FP(stderr, "%s: C_ACCEPT %s\n", term_server, c+1); /* Get the socket to try and accept() */ /* on. */ s = atoi((char *) (c+1)); /* Error checking.. */ if (s < 0 || s >= MAX_CLIENTS || clients[s].fd < 0 || clients[s].cl_type != CL_BOUND) { errno = 0; ret_fail(cl, local, 1, "Client out of range"); break; } /* try the actual accept(). */ s = accept(clients[s].fd , (struct sockaddr *) &dummy, &din); if (s < 0) { ret_fail(cl, local, 1, "Accept failed"); break; } set_nonblock(s); DEBUG_FP(stderr,"%s:got C_ACCEPT\n", term_server); if (cl->fd>=0) close(cl->fd); cl->fd = s; cl->cl_type = CL_SOCKET; DEBUG_FP(stderr, "%s:name is %s\n", term_server, c + 1); cl->type = T_RDFILE | T_WRFILE; cl->state = 1; ret_ok(cl, local); } break; #ifndef X_STREAMS_PIPE case C_X_SERVER: #endif case C_SOCKET: DEBUG_FP(stderr, "%s: C_SOCKET %s\n", term_server, c+1); { int s; s = open_unix((char *)(c+1)); if (s < 0) { DEBUG_FP(stderr, "%s:Open_unix failed (%s)\n", term_server, c+1); ret_fail(cl, local, 1, "open_unix() failed"); break; } set_nonblock(s); DEBUG_FP(stderr,"%s:got C_SOCKET\n", term_server); if (cl->fd>=0) close(cl->fd); cl->fd = s; cl->cl_type = CL_SOCKET; DEBUG_FP(stderr, "%s:name is %s\n", term_server, c + 1); cl->type = T_RDFILE | T_WRFILE; cl->state = 1; ret_ok(cl, local); } break; #ifdef X_STREAMS_PIPE case C_X_SERVER: DEBUG_FP(stderr, "%s: C_X_SERVER %s\n", term_server, c+1); { int s; int display_num; char *display, *screen; /* check DISPLAY variable for screen number */ display = getenv("DISPLAY"); if (display == 0) display = ":0"; screen = strchr(display, ':'); if (screen != 0) display_num = atoi(screen + 1); else display_num = 0; s = MakeStreamPipeConnection(display_num); if (s < 0) { DEBUG_FP(stderr, "%s: X connection failed (%s)\n", term_server, c+1); ret_fail(cl, local, 1, "X connection failed"); break; } set_nonblock(s); DEBUG_FP(stderr,"%s:got C_X_SERVER\n", term_server); if (cl->fd>=0) close(cl->fd); cl->fd = s; cl->cl_type = CL_SPIPE; DEBUG_FP(stderr, "%s:name is %s\n", term_server, c + 1); cl->type = T_RDFILE | T_WRFILE; cl->state = 1; ret_ok(cl, local); } break; #endif case C_PORT: DEBUG_FP(stderr, "%s: C_PORT %s\n", term_server, c+1); { struct sockaddr_in addr; struct hostent *hp, *gethostbyname(), hs; char hostname[258]; int s; char *colon, *portname; if ((s = socket(AF_INET, SOCK_STREAM, 0 )) < 0) { ret_fail(cl, local, 1, "Socket() failed"); break; } strcpy(hostname, (char *) (c+1)); colon = strchr(hostname, ':'); if (colon) { *colon = '\0'; portname = colon + 1; } else { #ifdef SYSV uname(&unam); strcpy(hostname, unam.nodename); #else gethostname (hostname, sizeof(hostname)); #endif portname = (char *)(c+1); } if (hostname[0] >= '0' && hostname[0] <= '9') { addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(hostname); } else { hp=gethostbyname(hostname); if (!hp) { ret_fail(cl, local, 1, "gethostbyname() failed"); perror ("gethostbyname"); close(s); break; } hs = *hp; addr.sin_family = hs.h_addrtype; addr.sin_addr = * ((struct in_addr *) hs.h_addr); } addr.sin_port = htons(atoi(portname)); DEBUG_FP(stderr, "Connecting to host %s port %d\n", hostname, atoi(portname) ); #ifdef CONN_NONBLOCK /* Set nonblock mode before connecting so connect() returns right away */ set_nonblock(s); #endif if (connect(s,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0) { #ifdef CONN_NONBLOCK if (errno != EINPROGRESS) { #endif ret_fail(cl,local, 1, "connect() failed"); perror ("connect"); close(s); break; #ifdef CONN_NONBLOCK } #endif } #ifndef CONN_NONBLOCK set_nonblock(s); #endif DEBUG_FP(stderr,"%s:got C_PORT\n", term_server); if (cl->fd>=0) close(cl->fd); cl->fd = s; cl->cl_type = CL_SOCKET; cl->type = T_RDFILE | T_WRFILE; #ifdef CONN_NONBLOCK cl->state = 5; cl->timeout = current_time + 20*30; /* 30-second connect timeout */ #else cl->state = 1; ret_ok(cl, local); #endif } break; case C_PRIORITY: DEBUG_FP(stderr, "%s: C_PRIORITY %d\n", term_server, atoi((char *) (c+1))); cl->priority = atoi((char *) (c+1)); ret_ok(cl, local); break; case C_COMPRESS: DEBUG_FP(stderr, "%s: C_COMPRESS %c\n", term_server, c[1]); switch(c[1]) { case 'y': /* yes */ case 'c': /* compress */ case 'Y': /* caps too */ case 'C': case 1: /* true */ case '1': /* in ascii */ cl->compress = 1; ret_ok(cl, local); break; case 'n': /* no */ case 'u': /* uncompress */ case 'r': /* raw */ case 'N': /* caps too */ case 'U': case 'R': case 0: /* false */ case '0': /* in ascii */ cl->compress = 0; ret_ok(cl, local); break; default: ret_fail(cl, local, 0, "Invalid argument"); break; } break; case C_STAT: { struct stat st; int type, permissions; char buff[10]; DEBUG_FP(stderr, "%s: C_STAT %s\n", term_server, c+1); if (stat((char *) (c+1), &st)< 0) { ret_fail(cl, local, 0, "stat() failed"); DEBUG_FP(stderr, "%s: stat() failed\n", term_server); break; } add_ret_buff(cl, local,SWITCH); add_ret_buff(cl, local,SWITCH-3); add_ret_buff(cl, local,I_OK); /* Get type */ if (S_ISREG(st.st_mode)) type = 0; else if (S_ISDIR(st.st_mode)) type = 1; else type = 2; /* Now get permissions. */ permissions = st.st_mode; if (getuid() == st.st_uid) permissions >>=6; else if (getgid() == st.st_gid) permissions >>=3; permissions &= 07; sprintf(buff, "%d %d %d", st.st_size, type, permissions); add_ret_buff_str(cl, local, buff); add_ret_buff(cl, local,0); break; } case C_STATS: { int opt; un_char ret[2000]; /* extern do_stats(un_char *, int, struct Client *); */ DEBUG_FP(stderr, "%s:C_STATS\n", term_server); opt = atoi((char *) (c+1)); add_ret_buff(cl, local, SWITCH); add_ret_buff(cl, local, SWITCH-3); add_ret_buff(cl, local, I_OK); do_stats(ret, opt, cl); add_ret_buff_str(cl, local, (char *)ret); add_ret_buff(cl, local, 0); break; } case C_SEEK: DEBUG_FP(stderr, "%s:C_SEEK %s\n", term_server, c+1); i = atoi((char *) (c+1)); if (lseek(cl->fd, i, 0)< 0) { ret_fail(cl, local, 0, "lseek() failed"); DEBUG_FP(stderr, "C_seek failed\n"); break; } ret_ok(cl, local); break; #ifdef USE_SIGWINCH case C_RESIZE: DEBUG_FP(stderr, "%s:C_RESIZE %s\n", term_server, c+1); { void do_resize(int number, int rows, int cols, int ypixels, int xpixels); int number; int rows, cols, ypixels, xpixels; sscanf((char *) (c+1), "%d %d %d %d %d", &number, &rows, &cols, &ypixels, &xpixels); do_resize(number, rows, cols, ypixels, xpixels); ret_ok(cl, local); } break; #endif /* USE_SIGWINCH */ case C_BINDN: /* ftp special -ot */ DEBUG_FP(stderr, "%s: C_BINDN\n", term_server); { int s, k; struct hostent *hp, *gethostbyname(); struct sockaddr_in addr; char hostname[258]; char *a, *p; #ifdef SYSV uname(&unam); strcpy(hostname, unam.nodename); #else gethostname(hostname, sizeof(hostname)); #endif /* SYSV */ hp=gethostbyname(hostname); if (!hp) { ret_fail(cl, local, 1, "gethostbyname() failed"); perror ("gethostbyname"); break; } s = bind_tcp(0); if (s < 0) { ret_fail(cl, local , 1, "bind_tcp() failed"); DEBUG_FP(stderr, "%s:Bind_tcp failed\n", term_server); break; } k=sizeof(addr); if (getsockname(s, (struct sockaddr *)&addr, &k) < 0) { ret_fail(cl, local, 1, "getsockname() failed"); DEBUG_FP(stderr, "%s:getsockname failed\n", term_server); break; } DEBUG_FP(stderr, "%s:sockname returned %lX,%lu\n", term_server, addr.sin_addr.s_addr, addr.sin_port); ret_ok(cl, local); a = (char *)hp->h_addr; p = (char *)&addr.sin_port; #define UC(y) (int) ((int) 0xff & (int) (y)) (void) sprintf(hostname, "%d,%d,%d,%d,%d,%d", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); for (k =0 ;hostname[k];++k) ADD_IN_BUFF(cl, hostname[k]); ADD_IN_BUFF(cl, 0); set_nonblock(s); if (cl->fd>=0) close(cl->fd); cl->fd = s; cl->cl_type = CL_BOUND; cl->type = T_RDFILE | T_WRFILE; cl->state = 1; } break; case C_QUIT: DEBUG_FP(stderr, "%s: C_QUIT\n", term_server); do_shutdown = 1; ret_ok(cl, local); break; default: break; } /* switch */ } /* function */ void init_client(struct Client *cl) { extern int compressing; /* the default from main */ cl->type = T_RDFILE | T_WRFILE; cl->dump_count = 0; cl->cl_type = CL_SOCKET; cl->state = 1; cl->compress = compressing; cl->c_state = 0; cl->number = cl - &clients[0]; cl->priority = 2; /* default priority. Higher is better. */ cl->name[0] = 0; clear_buffers(cl); DEBUG_LINK(stderr, "Init client %d\n", cl->number); } /*---------------------------------------------------------------------------*/ /* Returns next client to read. Will be beefed up later to support priorities*/ /* A client with a priority of n will get n/(sum) of the packets available */ /* maybe :) */ struct Client * get_next_client(void) { int i, h = -10000, j=0; static int c = 0; /* Check to see if any of the clients */ /* are closeing. This gets priority. */ for (i = 0; i < MAX_CLIENTS;++i) if (clients[i].state == 2) { DEBUG_STATE(stderr, "get_n_c ret cl %d\n", i); return &clients[i]; } /* Then run down the clients looking */ /* for the next ready client with the */ /* highest priority. */ for (i = 0; i < MAX_CLIENTS;++i, c = (c+1) % MAX_CLIENTS) if (clients[c].in_buff.size) { if (clients[c].priority > h) { h = clients[c].priority; j = c; } } if (h == -10000) return 0; /* No client was ready. */ c = (j+1) % MAX_CLIENTS; /* Start searching at next client next */ /* time. */ return &clients[j]; } int get_client_data(struct Client *cl) { int i; /* If nothing ready, signal that. */ SANITY(cl); if (!cl->in_buff.size) return -1; SANITY(cl->in_buff.end < cl->in_buff.alloced); SANITY(cl->in_buff.end >= 0); SANITY(cl->in_buff.size >=0); SANITY(cl->in_buff.size < cl->in_buff.alloced); /* get the next byte from the buffer.*/ i = cl->in_buff.data[cl->in_buff.end++]; if (cl->in_buff.end == cl->in_buff.alloced) /* Wrap the buffer */ /* round if we have */ /* hit the end. */ cl->in_buff.end = 0; cl->in_buff.size --; /* Update count of bytes left in buffer. */ SANITY(cl->dump_count >=0 ); if (cl->dump_count) { /* If we are currently dumping, update */ /* the dump count, and go smart if we */ /* have finished. */ if (!--cl->dump_count) cl->type |= T_SMART; } DEBUG_STATE(stderr, "\tgcd:%d\n", i); return i; } void put_client_data(struct Client *cl, int i) { while (1) { DEBUG_STATE(stderr, "p_c_d: %d: state %d i %d c_len %d\n", cl->number, cl->c_state, i, cl->c_len); switch (cl->c_state) { case 0: if (i == SWITCH) { cl->c_state = 1; return; } ADD_BUFF(cl, i); return; case 1: if (i == SWITCH) { ADD_BUFF(cl, i); cl->c_state = 0; return ; } cl->c_len = 0; /* yes. We do want to throw this byte away */ /* It is just a remote control message flag */ if (i == SWITCH - 3) { /* It is a result message. */ if (cl->type & T_SMART) { ADD_BUFF(cl, SWITCH); ADD_BUFF(cl, SWITCH-3); } cl->c_state = 3; } else { cl->c_state = 2; } return; case 2: cl->control[cl->c_len++] = i; if (i) return; do_control(0, cl , cl->control); cl->c_state = 0; return; case 3: if (cl->type & T_SMART) ADD_BUFF(cl, i); if (i) return; cl->c_state = 0; return; default: cl->c_state = 0; break; } } } /* Return the next byte that should go down the serial link */ /* Another bloody finite state machine. ;) */ /* This was a bitch to write. Sigh. More things than I thought needed */ /* to be handled. And it still isn't perfect. :( */ int get_client_byte() { static int state = 0, next, max; static char control[255]; struct Client *cl; int i; int first = 1; while (1) { DEBUG_STATE(stderr, "get_c_b: state %d next %d max %d cl %d\n", state, next, max, !curr_client ? -1 : curr_client->number); first = 0; switch(state) { case 0: /* looking for new client */ new_packet = 0; cl = get_next_client(); if (cl == 0) return -1; if (!cl->in_buff.size && cl->state == 2) { /* closeing down */ /* We have emptied the buffers, so */ /* just tell the remote end that , and */ /* finish off. */ DEBUG_FP(stderr, "%s:sending C_CLOSE\n", term_server); /* ++kay: should be state 3, not -1 */ cl->state = 3; state = 4; sprintf(control, "%c%c%c%c%c%c", SWITCH, cl->number + SWITCH + 1, SWITCH, SWITCH - 2, C_CLOSE, 0); curr_client = cl; next = 0; max = 6; break; } if (cl != curr_client) { curr_client = cl; state = 1; return SWITCH; } state = 2; break; case 1: state = 2; return curr_client->number + SWITCH + 1; break; case 2: /* If we are a new packet, then check */ /* to see if there is a new client */ /* with a greater probability. */ if (new_packet) { new_packet = 0; state = 0; break; } i = get_client_data(curr_client); if (i == SWITCH) { if (!(curr_client->type & T_SMART)) { state = 4; max = 1; next = 0; control[0] = SWITCH; return SWITCH; } state = 3; break; } else if (i < 0) { state = 0; break; } { extern int stat_cooked_out; ++stat_cooked_out; } return i; break; case 3: i = get_client_data(curr_client); if (i < 0) return -1; if (i == SWITCH) { /* It is an escaped escape code */ state = 4; max = 1; next = 0; return SWITCH; } /* ok. We have some sort of control message */ if ( i > SWITCH) { /* Hmm. It is trying to switch streams on it's own. welllll. ok. */ /* we'll let it. Note that is can only reliably insert 1 byte at */ /* a time */ control[0] = i; next = 0; max = 1; state = 4; return SWITCH; break; } if ( i != SWITCH - 1) { /* stuff for remote. Just pass it thru */ /* might not be SWITCH - 2, but if it isn't, we don't want */ /* to know. ;) */ control[0] = i; next = 0; max = 1; state = 4; return SWITCH; break; } /* ok. a real control message for us. */ state = 5; next = 0; break; case 4: if (next + 1 == max) state = 2; return control[next++]; break; case 5: /* A local control message */ /* note that there is a nasty bit here. All other streams block while */ /* we are waiting for this control message. I thought about */ /* programming around this but decided that it was too messy. */ /* note that most local control messages don't make much sense. */ i = get_client_data(curr_client); if (i < 0) return -1; control[next++] = i; /* If this isn't the end of the */ /* message, keep going.. */ if (i) break; do_control(1, curr_client, (un_char *) control); state = 2; break; } /* switch */ } /* while */ } /*----------------------------------------------------------------------*/ /* Transfers the next 'len' bytes from the link, to clients. Note that */ /* we handle control information here. */ void put_data(un_char *b, int len) { static struct Client *curr_client = 0; static int state = 0; int i, d; i = 0; while (i < len) { DEBUG_STATE(stderr, "put_d: s %d, cl %d d %d\n", state, !curr_client ? 0 : curr_client->number, b[i]); switch(state) { case 0: d = b[i++]; if (d == SWITCH) { state = 1; break; } /* ok. Just put data to current client */ if (!curr_client) break; put_client_data(curr_client, d); break; case 1: d = b[i++]; /* checked for escaped escape */ if (d == SWITCH) { if (curr_client) { put_client_data(curr_client, d); put_client_data(curr_client, d); } state = 0; break; } /* check for stream switch */ if (d > SWITCH) { curr_client = &clients[d - SWITCH - 1]; if (curr_client->state < 0) init_client(curr_client); DEBUG_LINK(stderr, "Stream switch to %d\n", d - SWITCH - 1); state = 0; break; } if (curr_client) { put_client_data(curr_client, SWITCH); put_client_data(curr_client, d); } state = 0; break; default: state = 0; break; } /* switch */ } /* while */ } /* function */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.