/* bsd.c */ /* $Id: bsd.c,v 1.35 1993/12/19 17:59:49 nils Exp $ */ #include "config.h" #include "externs.h" time_t now; #if defined(SYSV) || defined(HPUX) || defined(SYSV_MAYBE) # define mklinebuf(fp) setvbuf(fp, NULL, _IOLBF, 0) #else # define mklinebuf(fp) setlinebuf(fp) #endif #include <sys/ioctl.h> #include <sys/wait.h> #include <sys/file.h> #include <fcntl.h> #include <sys/errno.h> #include <sys/signal.h> #include "interface.h" #include <netdb.h> #include <ctype.h> /* #include <netinet/in.h> ? */ /* #include <sys/socket.h> */ extern int errno; extern int reserved; #define DEF_MODE 0644 static char *def_db_in = DEF_DB_IN; static char *def_db_out = DEF_DB_OUT; static char *def_logfile = DEF_LOGFILE; static int tinyport = TINYPORT; int shutdown_flag = 0; int exit_status = 136; /* we will change this when we set * shutdown_flag to 1... */ int sig_caught = 0; time_t muse_up_time; static char *connect_fail = "Either that player does not exist, or has a different password.\n"; #ifndef WCREAT static char *create_fail = "Either there is already a player with that name, or that name is illegal.\n"; #endif static char *flushed_message = "<Output Flushed>\n"; static char *shutdown_message = "Going down - Bye\n"; static char *get_password = "Please enter password:\n\373\001"; static char *got_password = "\374\001"; struct descriptor_data *descriptor_list = 0; static int sock; int ndescriptors = 0; char ccom[1024]; dbref cplr; int guest_free[NUMBER_GUESTS]; static void check_connect P((struct descriptor_data *, char *)); static void parse_connect P((char *, char *, char *, char *)); static void set_userstring P((char **, char *)); static int do_command P((struct descriptor_data *, char *)); static char *strsave P((char *)); static int process_input P((struct descriptor_data *)); #define MALLOC(result, type, number) do { \ if (!((result) = (type *) malloc ((number) * sizeof (type)))) \ panic("Out of memory"); \ } while (0) #define FREE(x) (free((void *) x)) void main(argc, argv) int argc; char *argv[]; { init_args(argc, argv); init_io(); printf("--------------------------------\n"); printf("MUSE online (pid=%d)\n", getpid()); init_attributes(); init_mail(); if ( init_game(def_db_in, def_db_out) < 0 ) { log_error(tprintf("Couldn't load %s!", def_db_in)); exit_nicely(136); } set_signals(); save_muse_pid(); time(&muse_up_time); /* main engine call */ shovechars(tinyport); log_important("Shutting down normally"); close_sockets(); do_haltall(1); dump_database(); free_database(); free_mail(); free_hash(); close(sock); if ( sig_caught > 0 ) log_important(tprintf("Shutting down due to signal %d", sig_caught)); exit_nicely(exit_status); } void save_muse_pid() { FILE *fp; struct stat statbuf; if (stat(MUSE_PID_FILE,&statbuf)>=0) { /* it already exists! */ log_error("muse pid file already exists!"); exit_nicely(136); } unlink(MUSE_PID_FILE); if ( (fp = fopen(MUSE_PID_FILE, "w")) != NULL ) { fprintf(fp, "%d\n", getpid()); fclose(fp); } } void remove_muse_pid() { FILE *fp; char buf1[20], buf2[20]; close(reserved); if (!(fp = fopen(MUSE_PID_FILE, "r"))) { perror("muse pid file"); log_error("can't open muse pid file!"); return; } fgets(buf1, 20, fp); sprintf(buf2, "%d\n", getpid()); if (strcmp(buf1, buf2)) { log_error(tprintf("muse pid file doesn't contain me(%d)!",getpid())); fclose(fp); return; } fclose(fp); unlink(MUSE_PID_FILE); reserved = open("/dev/null", O_RDWR, 0); } void init_args(argc, argv) int argc; char *argv[]; { /* change default input database? */ if ( argc > 1 ) --argc, def_db_in = *++argv; /* change default dump database? */ if ( argc > 1 ) --argc, def_db_out = *++argv; /* change default log file? */ if ( argc > 1 ) --argc, def_logfile = *++argv; /* change port number? */ if ( argc > 1 ) --argc, tinyport = atoi(*++argv); } void init_io() { #ifndef fileno /* sometimes it's #defined in stdio.h */ extern int fileno P((FILE *)); #endif int fd; /* close standard input */ fclose(stdin); /* open a link to the log file */ fd = open(def_logfile, O_WRONLY | O_CREAT | O_APPEND, DEF_MODE); if ( fd < 0 ) { perror("open()"); log_error(tprintf("error opening %s for writing", def_logfile)); exit_nicely(136); } /* attempt to convert standard output to logfile */ close(fileno(stdout)); if ( dup2(fd, fileno(stdout)) == -1 ) { perror("dup2()"); log_error("error converting standard output to logfile"); } mklinebuf(stdout); /* attempt to convert standard error to logfile */ close(fileno(stderr)); if ( dup2(fd, fileno(stderr)) == -1 ) { perror("dup2()"); printf("error converting standard error to logfile\n"); } mklinebuf(stderr); /* this logfile reference is no longer needed */ close(fd); /* save a file descriptor */ reserved = open("/dev/null",O_RDWR, 0); } int floating_x; void handle_exception() { floating_x = 1; } void set_signals() { /* we don't care about SIGPIPE, we notice it in select() and write() */ signal(SIGPIPE, SIG_IGN); #ifdef SYSV signal (SIGCLD, SIG_IGN); #endif /* standard termination signals */ signal (SIGINT, bailout); /* catch these because we might as well */ /* signal (SIGQUIT, bailout); signal (SIGILL, bailout); signal (SIGTRAP, bailout); signal (SIGIOT, bailout); signal (SIGEMT, bailout); signal (SIGFPE, bailout); signal (SIGBUS, bailout); signal (SIGSEGV, bailout); signal (SIGSYS, bailout); signal (SIGTERM, bailout); signal (SIGXCPU, bailout); signal (SIGXFSZ, bailout); signal (SIGVTALRM, bailout); signal (SIGUSR2, bailout); * want a core dump for now!! */ /* status dumper (predates "WHO" command) */ signal(SIGUSR1, dump_status); signal (SIGHUP, sig_handler); signal (SIGTERM, sig_handler); signal (SIGEMT, handle_exception); } void sig_handler(sig) int sig; { sig_caught = sig; if(sig == 15) exit_status = 0; else exit_status = 1; shutdown_flag = 1; } void raw_notify(player,msg) dbref player; char *msg; { struct descriptor_data *d; if (!(db[player].flags & PLAYER_CONNECT)) return; for(d = descriptor_list; d; d = d->next) { if (d->state==CONNECTED && d->player == player) { queue_string(d, msg); queue_write(d, "\n", 1); } } } struct timeval timeval_sub(now,then) struct timeval now; struct timeval then; { now.tv_sec -= then.tv_sec; now.tv_usec -= then.tv_usec; while (now.tv_usec < 0) { now.tv_usec += 1000000; now.tv_sec--; } if(now.tv_sec<0) now.tv_sec = 0; /* aack! */ return now; } int msec_diff(now,then) struct timeval now; struct timeval then; { return ((now.tv_sec - then.tv_sec) * 1000 + (now.tv_usec - then.tv_usec) / 1000); } struct timeval msec_add(t,x) struct timeval t; int x; { t.tv_sec += x / 1000; t.tv_usec += (x % 1000) * 1000; if (t.tv_usec >= 1000000) { t.tv_sec += t.tv_usec / 1000000; t.tv_usec = t.tv_usec % 1000000; } return t; } struct timeval update_quotas(last,current) struct timeval last; struct timeval current; { int nslices; struct descriptor_data *d; nslices = msec_diff (current, last) / COMMAND_TIME_MSEC; if (nslices > 0) { for (d = descriptor_list; d; d = d -> next) { d -> quota += COMMANDS_PER_TIME * nslices; if (d -> quota > COMMAND_BURST_SIZE) d -> quota = COMMAND_BURST_SIZE; } } return msec_add (last, nslices * COMMAND_TIME_MSEC); } int need_more_proc; int maxd; void shovechars(port) int port; { fd_set input_set, output_set; struct timeval last_slice, current_time; struct timeval next_slice; struct timeval timeout, slice_timeout; int found; struct descriptor_data *d, *dnext; struct descriptor_data *newd; int avail_descriptors; log_io(tprintf("Starting up on port %d",port)); sock = make_socket (port); maxd = sock+1; gettimeofday(&last_slice, (struct timezone *) 0); avail_descriptors = getdtablesize() - 5; #ifdef USE_RWHO rwhocli_setup(RWHO_SERVER,RWHO_PASSWORD,MUSE_NAME,BASE_VERSION); #endif while (shutdown_flag == 0) { gettimeofday(¤t_time, (struct timezone *) 0); last_slice = update_quotas (last_slice, current_time); if (loading_db) { process_commands(); if (shutdown_flag) break; /* test for events */ dispatch(); } else { load_more_db(); } /* any queued robot commands waiting? */ timeout.tv_sec = (need_more_proc||test_top()) ? 0 : 100; need_more_proc=0; timeout.tv_usec = 5; next_slice = msec_add (last_slice, COMMAND_TIME_MSEC); slice_timeout = timeval_sub (next_slice, current_time); FD_ZERO (&input_set); FD_ZERO (&output_set); if (ndescriptors < avail_descriptors && sock>=0) FD_SET (sock, &input_set); for (d = descriptor_list; d; d=dnext) { dnext = d->next; if(d->cstatus&C_REMOTE && d->output.head) { if(!process_output(d)) shutdownsock(d); need_more_proc=1; } } for (d = descriptor_list; d; d=d->next) if(!(d->cstatus&C_REMOTE)) { if (d->input.head) timeout = slice_timeout; else FD_SET (d->descriptor, &input_set); if (d->output.head && (d->state!=CONNECTED || d->player>0)) FD_SET (d->descriptor, &output_set); } if ((found=select (maxd, &input_set, &output_set, (fd_set *) 0, &timeout)) < 0) { if (errno != EINTR) { perror ("select"); /* return;*/ /* naw.. stay up. */ } } else { /* if !found then time for robot commands */ if (loading_db && !found) { if(do_top() && do_top()) do_top(); continue; } (void) time (&now); if (sock>=0 && FD_ISSET (sock, &input_set)) { if (!(newd = new_connection (sock))) { if (errno && errno != EINTR && errno != EMFILE && errno != ENFILE) { perror ("new_connection"); /* return;*/ /* naw.. stay up. */ } } else { if (newd->descriptor >= maxd) maxd = newd->descriptor + 1; } } for (d = descriptor_list; d; d=dnext) { dnext=d->next; if (FD_ISSET (d->descriptor, &input_set) && !(d->cstatus&C_REMOTE)) if (!process_input (d)) shutdownsock(d); } for (d = descriptor_list; d; d = dnext) { dnext = d->next; if(d->cstatus&C_REMOTE) process_output(d); } for (d = descriptor_list; d; d = dnext) { dnext = d->next; if(!(d->cstatus&C_REMOTE) && (FD_ISSET (d->descriptor, &output_set))) if (!process_output(d)) shutdownsock(d); } for (d = descriptor_list; d; d = dnext) { dnext = d->next; if(d->cstatus&C_REMOTE && !d->parent) shutdownsock(d); } } } } struct descriptor_data *new_connection(sock) int sock; { int newsock; struct sockaddr_in addr; int addr_len; addr_len = sizeof (addr); newsock = accept (sock, (struct sockaddr *) & addr, &addr_len); if (newsock < 0) { if(errno==EALREADY) { /* screwy sysv buf. */ static int k=0; if(k++>50) { log_error("Killin' EALREADY. restartin' socket."); puts("Killin' EALREADY. restartin' socket."); close(sock); sock = make_socket(4201); k=0; } } return 0; #ifdef LOCKOUT } else if(forbidden_site(ntohl(addr.sin_addr.s_addr))) { log_io(tprintf("REFUSED CONNECTION from %s(%d) on descriptor %d", addrout(addr.sin_addr.s_addr), ntohs(addr.sin_port), newsock)); shutdown(newsock, 0); close(newsock); errno = 0; return 0; #endif /* LOCKOUT */ } else { struct hostent *hent; char buff[100]; time_t tt; hent = gethostbyaddr (&(addr.sin_addr.s_addr), sizeof (addr.sin_addr.s_addr), AF_INET); if (hent) strcpy (buff, hent->h_name); else { extern char *inet_ntoa(); strcpy(buff, inet_ntoa(addr.sin_addr.s_addr)); } tt = time(NULL); log_io(tprintf("USER CONNECT: des: %d host %s time: %s", newsock,buff,ctime(&tt))); return initializesock (newsock, &addr,buff); } } char *addrout(a) long a; { #ifdef HOST_NAME extern char *inet_ntoa(); struct hostent *he; a = htonl(a); he = gethostbyaddr(&a,sizeof(a),AF_INET); if (he) return he->h_name; else return inet_ntoa(a); #else /* return inet_ntoa(a);*/ return "OOPS"; #endif /* HOST_NAME */ } void clearstrings(d) struct descriptor_data *d; { if (d->output_prefix) { FREE(d->output_prefix); d->output_prefix = 0; } if (d->output_suffix) { FREE(d->output_suffix); d->output_suffix = 0; } } void shutdownsock(d) struct descriptor_data *d; { int count; dbref guest_player; struct descriptor_data *sd; /* if this is a guest player, save his reference # */ guest_player = NOTHING; if (d->state == CONNECTED) if(d->player>0) if(Guest(d->player)) guest_player = d->player; if (d->state == CONNECTED) { if(d->player>0) { log_io (tprintf("DISCONNECT descriptor %d player %s(%d)", d->descriptor, db[d->player].name, d->player)); announce_disconnect(d->player); } } else { log_io (tprintf("DISCONNECT descriptor %d never connected", d->descriptor)); } clearstrings(d); if(!(d->cstatus&C_REMOTE)) { shutdown(d->descriptor, 0); close(d->descriptor); } else { register struct descriptor_data *k; for(k=descriptor_list; k; k=k->next) if(k->parent==d) k->parent = 0; } freeqs(d); *d->prev = d->next; if (d->next) d->next->prev = d->prev; if(!(d->cstatus&C_REMOTE)) ndescriptors--; FREE(d); /* if this is a guest account and the last to disconnect from it, kill it */ if ( guest_player != NOTHING ) { count = 0; for (sd = descriptor_list; sd; sd = sd->next) { if (sd->state == CONNECTED && sd->player == guest_player) ++count; } if ( count == 0 ) destroy_guest(guest_player); } } void rwho_setupfd(player,fd) dbref player; int fd; { struct descriptor_data *d; ndescriptors++; MALLOC(d, struct descriptor_data, 1); d->descriptor = fd; d->concid = make_concid(); d->cstatus = 0; d->parent = 0; d->state = CONNECTED; make_nonblocking(fd); d->player = -player; d->output_prefix=0; d->output_suffix=0; d->output_size=0; d->output.head=0; d->output.tail = &d->output.head; d->input.head = 0; d->input.tail = &d->input.head; d->raw_input = 0; d->raw_input_at = 0; d->quota=COMMAND_BURST_SIZE; d->last_time=0; /* d->address=inet_addr("127.0.0.1"); * localhost. cuz. */ strcpy(d->addr,"RWHO"); if (descriptor_list) descriptor_list->prev = &d->next; d->next = descriptor_list; d->prev = &descriptor_list; d->edit_buff = NULL; descriptor_list = d; if (fd >= maxd) maxd = fd + 1; } struct descriptor_data *initializesock(s,a,addr) int s; struct sockaddr_in *a; char *addr; { struct descriptor_data *d; /*fprintf(stderr,"3\n");fflush(stderr);*/ ndescriptors++; MALLOC(d, struct descriptor_data, 1); d->descriptor = s; d->concid = make_concid(); d->cstatus = 0; d->parent = 0; d->state = WAITCONNECT; make_nonblocking (s); d->output_prefix = 0; d->output_suffix = 0; d->output_size = 0; d->output.head = 0; d->output.tail = &d->output.head; d->input.head = 0; d->input.tail = &d->input.head; d->raw_input = 0; d->raw_input_at = 0; d->quota = COMMAND_BURST_SIZE; d->last_time = 0; strncpy(d->addr,addr,50); d->address = *a; /* added 5/3/90 SCG */ if (descriptor_list) descriptor_list->prev = &d->next; d->next = descriptor_list; d->prev = &descriptor_list; d->edit_buff = NULL; descriptor_list = d; welcome_user (d); return d; } int make_socket(port) int port; { int s; struct sockaddr_in server; int opt; s = socket (AF_INET, SOCK_STREAM, 0); if (s < 0) { perror (tprintf("creating stream socket on port %d",port)); #ifndef RESOCK exit_status = 1; /* try again. */ shutdown_flag = 1; #endif return -1; } opt = 1; if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof (opt)) < 0) { perror ("setsockopt"); #ifndef RESOCK shutdown_flag = 1; exit_status = 1; #endif close(s); return -1; } server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons (port); if (bind (s, (struct sockaddr *) & server, sizeof (server))) { perror ("binding stream socket"); close (s); #ifndef RESOCK shutdown_flag = 1; exit_status = 1; #endif return -1; } listen (s, 5); return s; } struct text_block *make_text_block(s,n) char *s; int n; { struct text_block *p; MALLOC(p, struct text_block, 1); MALLOC(p->buf, char, n); bcopy (s, p->buf, n); p->nchars = n; p->start = p->buf; p->nxt = 0; return p; } void free_text_block (t) struct text_block *t; { FREE (t->buf); FREE ((char *) t); } void add_to_queue(q,b,n) struct text_queue *q; char *b; int n; { struct text_block *p; if (n == 0) return; p = make_text_block (b, n); p->nxt = 0; *q->tail = p; q->tail = &p->nxt; } int flush_queue(q,n) struct text_queue *q; int n; { struct text_block *p; int really_flushed = 0; n += strlen(flushed_message); while (n > 0 && (p = q->head)) { n -= p->nchars; really_flushed += p->nchars; q->head = p->nxt; free_text_block (p); } p = make_text_block(flushed_message, strlen(flushed_message)); p->nxt = q->head; q->head = p; if (!p->nxt) q->tail = &p->nxt; really_flushed -= p->nchars; return really_flushed; } int queue_write(d,b,n) struct descriptor_data *d; char *b; int n; { int space; if (d->cstatus & C_REMOTE) need_more_proc=1; space = MAX_OUTPUT - d->output_size - n; if (space < 0) d->output_size -= flush_queue(&d->output, -space); add_to_queue (&d->output, b, n); d->output_size += n; return n; } int queue_string(d,s) struct descriptor_data *d; char *s; { return queue_write (d, s, strlen (s)); } int process_output(d) struct descriptor_data *d; { struct text_block **qp, *cur; int cnt; if(d->cstatus&C_REMOTE) { static char buf[10]; static char obuf[2048]; int buflen; int k,j; sprintf(buf,"%d ",d->concid); buflen=strlen(buf); bcopy(buf,obuf,buflen); j=buflen; for(qp= &d->output.head;(cur= *qp);) { need_more_proc=1; for(k=0;k<cur->nchars;k++) { obuf[j++]=cur->start[k]; if(cur->start[k]=='\n') { if(d->parent) queue_write(d->parent,obuf,j); bcopy(buf,obuf,buflen); j=buflen; } } d->output_size -= cur->nchars; if(!cur->nxt) d->output.tail=qp; *qp = cur->nxt; free_text_block(cur); } if(j>buflen) queue_write(d,obuf+buflen,j-buflen); return 1; } else { for (qp = &d->output.head; (cur = *qp);) { cnt = write (d->descriptor, cur -> start, cur -> nchars); if (cnt < 0) { if (errno == EWOULDBLOCK) return 1; return 0; } d->output_size -= cnt; if (cnt == cur -> nchars) { if (!cur -> nxt) d->output.tail = qp; *qp = cur -> nxt; free_text_block (cur); continue; /* do not adv ptr */ } cur -> nchars -= cnt; cur -> start += cnt; break; } } return 1; } void make_nonblocking(s) int s; { if (fcntl (s, F_SETFL, FNDELAY) == -1) { perror ("make_nonblocking: fcntl"); panic ("FNDELAY fcntl failed"); } } void freeqs(d) struct descriptor_data *d; { struct text_block *cur, *next; cur = d->output.head; while (cur) { next = cur -> nxt; free_text_block (cur); cur = next; } d->output.head = 0; d->output.tail = &d->output.head; cur = d->input.head; while (cur) { next = cur -> nxt; free_text_block (cur); cur = next; } d->input.head = 0; d->input.tail = &d->input.head; if (d->raw_input) FREE (d->raw_input); d->raw_input = 0; d->raw_input_at = 0; } void welcome_user(d) struct descriptor_data *d; { connect_message(d, WELCOME_MSG_FILE,0); } void connect_message(d, filename, direct) struct descriptor_data *d; char *filename; int direct; { int n, fd; char buf[512]; close(reserved); if ( (fd = open(filename, O_RDONLY, 0)) != -1 ) { while ( (n = read(fd, buf, 512)) > 0 ) queue_write(d, buf, n); close(fd); queue_write(d, "\n", 1); } reserved=open("/dev/null", O_RDWR, 0); if(direct) { process_output(d); } } char *strsave (s) char *s; { char *p; MALLOC (p, char, strlen(s) + 1); if (p) strcpy (p, s); return p; } void save_command (d,command) struct descriptor_data *d; char *command; { add_to_queue (&d->input, command, strlen(command)+1); } int process_input (d) struct descriptor_data *d; { char buf[1024]; int got; char *p, *pend, *q, *qend; got = read (d->descriptor, buf, sizeof buf); if (got <= 0) return 0; if (!d->raw_input) { MALLOC(d->raw_input,char,MAX_COMMAND_LEN); d->raw_input_at = d->raw_input; } p = d->raw_input_at; pend = d->raw_input + MAX_COMMAND_LEN - 1; for (q=buf, qend = buf + got; q < qend; q++) { if (*q == '\n') { *p = '\0'; if (p >= d->raw_input) save_command (d, d->raw_input); p = d->raw_input; } else if (p < pend && isascii (*q) && isprint (*q)) { *p++ = *q; } } if(p > d->raw_input) { d->raw_input_at = p; } else { FREE(d->raw_input); d->raw_input = 0; d->raw_input_at = 0; } return 1; } void set_userstring (userstring,command) char **userstring; char *command; { if (*userstring) { FREE(*userstring); *userstring = 0; } while (*command && isascii (*command) && isspace (*command)) command++; if (*command) *userstring = strsave (command); } void process_commands() { int nprocessed; struct descriptor_data *d, *dnext; struct text_block *t; do { nprocessed = 0; for (d = descriptor_list; d; d = dnext) { dnext = d->next; if (d -> quota > 0 && (t = d -> input.head)) { nprocessed++; if (!do_command(d, t -> start)) { connect_message(d,LEAVE_MSG_FILE,1); shutdownsock(d); } else { d -> input.head = t -> nxt; if (!d -> input.head) d -> input.tail = &d -> input.head; free_text_block (t); } } } } while (nprocessed > 0); } int do_command (d,command) struct descriptor_data *d; char *command; { d->last_time = now; d->quota--; depth=0; if (!*command && !(d->player<0 && d->state==CONNECTED)) return 1; if (d->state == CONNECTED && d->player > 0) { static char buf[2048]; sprintf(buf,"%05d: %s",d->player,command); log_typed(buf); } if (!strcmp (command, QUIT_COMMAND)) { return 0; } else if(!strncmp (command, "I wanna be a concentrator... my password is ", sizeof ("I wanna be a concentrator... my password is ") -1)) { do_becomeconc(d,command+sizeof("I wanna be a concentrator... my password is ")-1); } else if (! strncmp(command, WHO_COMMAND, sizeof(WHO_COMMAND)) && d->state != CONNECTED) { if (d->output_prefix) { queue_string (d, d->output_prefix); queue_write (d, "\n", 1); } dump_users (NOTHING,NULL,NULL,d); if (d->output_suffix) { queue_string (d, d->output_suffix); queue_write (d, "\n", 1); } } else if (!strncmp (command, PREFIX_COMMAND, strlen (PREFIX_COMMAND))) { set_userstring (&d->output_prefix, command+strlen(PREFIX_COMMAND)); } else if (!strncmp (command, SUFFIX_COMMAND, strlen (SUFFIX_COMMAND))) { set_userstring (&d->output_suffix, command+strlen(SUFFIX_COMMAND)); } else if (d->cstatus & C_CCONTROL) { if(!strcmp(command, "Gimmie a new concid")) do_makeid(d); else if(!strncmp(command, "I wanna connect concid ", sizeof ("I wanna connect concid ")-1)) { char *m,*n; m=command+sizeof("I wanna connect concid ")-1; n=strchr(m,' '); if(!n) queue_string(d,"Usage: I wanna connect concid <id> <hostname>\n"); else do_connectid(d,atoi (command+ sizeof ("I wanna connect concid ")-1),n); } else if(!strncmp(command, "I wanna kill concid ", sizeof ("I wanna kill concid ")-1)) do_killid(d, atoi(command+ sizeof ("I wanna kill concid ")-1)); else { char *k; k=strchr(command,' '); if(!k) queue_string(d,"Huh???\r\n"); else { struct descriptor_data *l; int j; *k = '\0'; j=atoi(command); for(l=descriptor_list;l;l=l->next) { if(l->concid==j) break; } if(!l) queue_string(d,"I don't know that concid.\r\n"); else { k++; if(!do_command(l,k)) { connect_message(l,LEAVE_MSG_FILE,1); shutdownsock(l); } } } } } else { if (d->state == CONNECTED) { if (d->output_prefix) { queue_string (d, d->output_prefix); queue_write (d, "\n", 1); } cplr=d->player; if(d->player>0) { strcpy(ccom,command); if (d->edit_buff == NULL) process_command (d->player, command,NOTHING); else edit_command(d, d->player, command); } else notify(-d->player,command); if (d->output_suffix) { queue_string (d, d->output_suffix); queue_write (d, "\n", 1); } } else { check_connect (d, command); } } return 1; } void check_connect (d,msg) struct descriptor_data *d; char *msg; { extern NALLOC *glurp; char *p; char command[MAX_COMMAND_LEN]; char user[MAX_COMMAND_LEN]; char password[MAX_COMMAND_LEN]; #ifdef EMAIL_CREATE char email[MAX_COMMAND_LEN]; #endif dbref player; if (d->state == WAITPASS) { /* msg contains the password. */ char *foobuf = na_alloc(glurp, MAX_COMMAND_LEN); sprintf(foobuf,"connect %s %s",d->charname, msg); free(d->charname); queue_string(d,got_password); d->state = WAITCONNECT; msg = foobuf; } parse_connect (msg, command, user, password #ifdef EMAIL_CREATE ,email #endif ); if (!strncmp (command, "co", 2)) { if ( string_prefix(user, GUEST_PREFIX) ) { if ( (p = make_guest(d)) == NULL ) return; strcpy(user, p); strcpy(password, GUEST_PREFIX); } player = connect_player (user, password); if (player == NOTHING && !*password) { /* hmm. bet they want to type it in seperately. */ queue_string(d, get_password); d->state = WAITPASS; d->charname = malloc(strlen(user)+1); strcpy(d->charname, user); return; } else if (player == NOTHING) { queue_string (d, connect_fail); log_io (tprintf("FAILED CONNECT %s on descriptor %d", user, d->descriptor)); } else { log_io (tprintf("CONNECTED %s(%d) on descriptor %d", db[player].name, player, d->descriptor)); if (d->state == WAITPASS) queue_string(d, got_password); d->state = CONNECTED; d->connected_at = time(0); d->player = player; /* give players a message on connection */ connect_message(d, CONNECT_MSG_FILE,0); announce_connect(player); do_look_around(player); /* XXX might want to add check for haven here. */ if(Guest(player)) notify(player, tprintf( "Welcome to %s; your name is %s", MUSE_NAME, db[player].name)); } } else if (!strncmp (command, "cr", 2)) { #ifdef WCREAT connect_message(d, REGISTER_MSG_FILE, 0); #else /* WCREAT */ #ifdef EMAIL_CREATE if(!email || !strcmp(email,"")) { queue_string(d,"You need to specify an email address.\n"); queue_string(d,"Try: create <name> <password> <email address>.\n"); } else #endif { player = create_player (user, password,CLASS_VISITOR); if (player == NOTHING) { queue_string (d, create_fail); log_io (tprintf("FAILED CREATE %s on descriptor %d", user, d->descriptor)); } else { log_io (tprintf("CREATED %s(%d) on descriptor %d", db[player].name, player, d->descriptor)); #ifdef EMAIL_CREATE atr_add(player,A_EMAIL,email); #endif d->connected = 1; d->connected_at = time(0); d->player = player; /* give new players a special message */ connect_message(d, NEW_CONNECT_MSG_FILE, 0); /* connect_message(d, CONNECT_MSG_FILE); */ announce_connect(player); do_look_around(player); } } #endif /* WCREAT */ } else welcome_user (d); if (d->state == WAITPASS) { d->state = WAITCONNECT; queue_string(d, got_password); } } void parse_connect (msg,command,user,pass #ifdef EMAIL_CREATE ,email #endif ) char *msg; char *command; char *user; char *pass; #ifdef EMAIL_CREATE char *email; #endif { char *p; while (*msg && isascii(*msg) && isspace (*msg)) msg++; p = command; while (*msg && isascii(*msg) && !isspace (*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && isspace (*msg)) msg++; p = user; while (*msg && isascii(*msg) && !isspace (*msg)) *p++ = *msg++; *p = '\0'; while (*msg && isascii(*msg) && isspace (*msg)) msg++; p = pass; while (*msg && isascii(*msg) && !isspace (*msg)) *p++ = *msg++; *p = '\0'; #ifdef EMAIL_CREATE while (*msg && isascii(*msg) && isspace(*msg)) msg++; p=email; while (*msg && isascii(*msg) && !isspace(*msg)) *p++ = *msg++; *p = '\0'; #endif } void mark_free(n) int n; { guest_free[n-1] = 1; } /* this algorithm can be changed later to accomodate an unlimited number of guests. currently, it supports a limited number. */ char *make_guest(d) struct descriptor_data *d; { int i; dbref player; char name[50]; static int need_init = 1; /* make sure flags are initialized */ if ( need_init ) { need_init = 0; for ( i = 0; i < NUMBER_GUESTS; i++ ) guest_free[i] = 1; } /* locate a free guest id - mark it non free */ for ( i = 0; i < NUMBER_GUESTS; i++ ) if ( guest_free[i] ) break; if ( i == NUMBER_GUESTS ) { queue_string(d, "All guest ID's are busy; please try again later.\n"); return NULL; } sprintf(name, "%s%d", GUEST_PREFIX, i+1); player = create_guest(name, GUEST_PREFIX); guest_free[i] = 0; if ( player == NOTHING ) { queue_string(d, "Error creating guest ID, please try again later.\n"); log_error(tprintf("Error creating guest ID. '%s' already exists.", name)); return NULL; } return db[player].name; } void close_sockets() { struct descriptor_data *d, *dnext; /* shutdown_flag = 1; * just in case. so announce_disconnect * does'nt reset the PLAYER_CONNECT * flags. */ for (d = descriptor_list; d; d = dnext) { dnext = d->next; if(!(d->cstatus&C_REMOTE)) { write (d->descriptor, shutdown_message, strlen (shutdown_message)); process_output(d); shutdownsock(d); } } } void emergency_shutdown() { log_error("Emergency shutdown."); shutdown_flag = 1; exit_status = 136; close_sockets(); } int boot_off(player) dbref player; { struct descriptor_data *d; for (d = descriptor_list; d; d = d->next) { if (d->state == CONNECTED && d->player == player) { process_output (d); shutdownsock(d); return 1; } } return 0; } signal_type bailout(sig) int sig; { char message[1024]; sprintf (message, "BAILOUT: caught signal %d", sig); panic(message); exit_nicely (136); #ifdef void_signal_type return; #else return 0; #endif } signal_type dump_status(int i) { struct descriptor_data *d; long now; now = time (NULL); fprintf (stderr, "STATUS REPORT:\n"); for (d = descriptor_list; d; d = d->next) { if (d->state == CONNECTED) { fprintf (stderr, "PLAYING descriptor %d player %s(%d)", d->descriptor, db[d->player].name, d->player); if (d->last_time) fprintf (stderr, " idle %d seconds\n", now - d->last_time); else fprintf (stderr, " never used\n"); } else { fprintf (stderr, "CONNECTING descriptor %d", d->descriptor); if (d->last_time) fprintf (stderr, " idle %d seconds\n", now - d->last_time); else fprintf (stderr, " never used\n"); } } #ifdef void_signal_type return; #else return 0; #endif } extern char *wholev(); #define WHO_BUF_SIZ 300 #define DEF_SCR_COLS 78 /* must be less than WHO_BUF_SIZ */ #define MIN_SEC_SPC 2 #define MIN_GRP_SPC 4 #define DEF_WHO_FLAGS "Nfoitd" /* close to the old WHO output format */ #define DEF_WHO_ALIAS "" /* what people see if no alias is set */ #define W_NAME 0x001 #define W_ALIAS 0x002 #define W_FLAGS 0x004 #define W_ONFOR 0x008 #define W_IDLE 0x010 #define W_CLASS 0x020 #define W_HOST 0x040 #define W_TALENT 0x080 #define W_DOING 0x100 static char who_flags[] = "nafoichtd"; static char *who_fmt_small[] = { "%-10.10s ", "%-10.10s ", "%-4.4s ", "%9.9s ", "%4.4s ", "%-5.5s ", "%-20.20s ", "%-15.15s ", "%-15.15s " }; static int who_sizes_small[] = { 10, 10, 3, 9, 4, 5, 20, 15, 15 }; static char *who_fmt_large[] = { "%-16.16s ", "%-16.16s ", "%-4.4s ", "%9.9s ", "%4.4s ", "%-13.13s ", #ifdef BRACKET_HOST "%-32.32s ", "%-15.15s ", "%-15.15s " }; static int who_sizes_large[] = { 16, 16, 3, 9, 4, 13, 32, 15, 15 }; char who_bh_buf[33]; #else /* BRACKET_HOST */ "%-30.30s ", "%-15.15s ", "%-15.15s " }; static int who_sizes_large[] = { 16, 16, 3, 9, 4, 13, 30, 15, 15 }; #endif /* BRACKET_HOST */ #define WHO_SIZE 9 static int who_sizes[WHO_SIZE]; static char *who_fmt[WHO_SIZE]; void dump_users(w,arg1,arg2,k) dbref w; char *arg1, *arg2; struct descriptor_data *k; /* if non-zero, use k instead of w */ { char *p, *b, *names; char buf[WHO_BUF_SIZ+1]; struct descriptor_data *d; char fbuf[5], flags[WHO_SIZE+1]; int done, pow_who, hidden = 0, see_player; int i, j, ni, bit, bits, num_secs, scr_cols, hc, header; int grp, grp_len, num_grps, total_count, hidden_count; dbref who, nl_size, *name_list; strcpy(flags,DEF_WHO_FLAGS); /* setup special data */ names = ""; scr_cols = DEF_SCR_COLS; if( ! k ) { if( Typeof(w) != TYPE_PLAYER && ! payfor(w, 50) ) { notify(w, "You don't have enough pennies."); return; } if ( *(p = atr_get(w, A_COLUMNS)) != '\0' ) { scr_cols = atoi(p); if ( scr_cols > WHO_BUF_SIZ+1 ) scr_cols = WHO_BUF_SIZ; } if ( *(p = atr_get(w, A_WHOFLAGS)) != '\0' ) { *flags = '\0'; strncat(flags, p, WHO_SIZE); } /* in case this value used, we must process names before atr_get is called again */ if ( *(p = atr_get(w, A_WHONAMES)) != '\0' ) names = p; } /* override data */ if ( arg1 != NULL ) if ( *arg1 != '\0' ) { *flags = '\0'; strncat(flags, arg1, WHO_SIZE); } if ( arg2 != NULL ) if ( *arg2 != '\0' ) names = arg2; /* Check for power to distinguish class names */ pow_who = ( k ) ? 0 : has_pow(w, NOTHING, POW_WHO); /* process flags */ bits = 0; grp_len = 0; num_secs = 0; for ( p = flags; *p != '\0'; p++ ) { for ( i = 0, bit = 1; i < WHO_SIZE; i++, bit <<= 1 ) if ( to_lower(*p) == who_flags[i] ) { ++num_secs; bits |= bit; if ( islower(*p) ) { who_sizes[i] = who_sizes_small[i]; who_fmt[i] = who_fmt_small[i]; } else { who_sizes[i] = who_sizes_large[i]; who_fmt[i] = who_fmt_large[i]; } grp_len += who_sizes[i]; break; } if ( i == WHO_SIZE ) { sprintf(buf, "%c: bad who flag.%s", (int) *p, (k) ? "\n" : ""); if ( k ) queue_string(k, buf); else notify(w, buf); return; } } /* process names (before atr_get called that overwrites static buffer) */ if ( *names == '\0' ) { /* avoid error msg in lookup_players() for missing name list */ nl_size = 0; name_list = &nl_size; } else { name_list = lookup_players((k) ? NOTHING : w, names); if (!name_list[0]) return; /* we tried, but no users matched. */ } /* add in min section spacing */ grp_len += (num_secs - 1) * MIN_SEC_SPC; /* calc # of groups */ num_grps = ((scr_cols - grp_len) / (grp_len + MIN_GRP_SPC)) + 1; if ( num_grps < 1 ) num_grps = 1; /* calc header size based on see-able users */ for ( hc = 0, d = descriptor_list; d; d = d->next ) { if ( d->state != CONNECTED || d->player <= 0 ) continue; if (*atr_get(d->player, A_LHIDE)) { if (k) continue; if (!controls(w,d->player,POW_WHO) && !could_doit(w,d->player,A_LHIDE)) continue; } if ( name_list[0] > 0 ) { for ( i = 1; i <= name_list[0]; i++ ) if ( d->player == name_list[i] ) break; if ( i > name_list[0] ) continue; } if ( ++hc >= num_grps ) break; } /* process names or, (if no names requested) loop once for all names */ b = buf; grp = 1; header = 1; ni = 1; who = NOTHING; done = 0; total_count = hidden_count = 0; while ( !done ) { /* if a player is on the names list, process them */ if ( ni <= name_list[0] ) who = name_list[ni++]; /* if no names left on list, this is final run through loop */ if ( ni > name_list[0] ) done = 1; /* Build output lines and print them */ d = descriptor_list; while ( d ) { if ( !header ) { /* skip records that aren't in use */ /* if name list, skip those not on list */ if ( d->state!=CONNECTED || d->player <= 0 || ( who != NOTHING && who != d->player) ) { d = d->next; continue; } /* handle hidden players, keep track of player counts */ hidden = 0; see_player = 1; if (k?*atr_get(d->player,A_LHIDE):!could_doit(w,d->player,A_LHIDE)) { hidden = 1; see_player = !k && controls(w, d->player, POW_WHO); } /* do counts */ if ( see_player || name_list[0] == 0 ) { ++total_count; if ( hidden ) ++hidden_count; } if ( !see_player ) { d = d->next; continue; } } /* build output line */ for ( i = 0, bit = 1; i < WHO_SIZE; i++, bit <<= 1 ) { switch ( bits & bit ) { case W_NAME: p = header ? "Name" : db[d->player].name; break; case W_ALIAS: if ( header ) p = "Alias"; else if ( *(p = atr_get(d->player, A_ALIAS)) == '\0' ) p = DEF_WHO_ALIAS; break; case W_FLAGS: if ( header ) p = "Flg"; else { fbuf[0] = (hidden) ? 'h' : ' '; fbuf[1] = (k?*atr_get(d->player,A_LPAGE): !could_doit(w,d->player,A_LPAGE)) ? 'H' : ' '; fbuf[2] = (db[d->player].flags & PLAYER_NO_WALLS) ? 'N' : ' '; fbuf[3] = (db[d->player].flags & PLAYER_NEWBIE) ? 'n' : ' '; fbuf[4] = '\0'; p = fbuf; } break; case W_TALENT: if ( header ) p = "Talent"; else p=atr_get(d->player,A_TALENT); break; case W_DOING: if (header) p = "Doing"; else p=atr_get(d->player,A_DOING); break; case W_ONFOR: p = header ? "On For" : time_format_1(now - d->connected_at); break; case W_IDLE: p = header ? "Idle" : time_format_2(now - d->last_time); break; case W_CLASS: if ( header ) p = "Class"; else if(pow_who) p = wholev(d->player,(who_sizes[i]>who_sizes_small[i]),pow_who); else p = "?"; break; case W_HOST: p = "?"; if ( header ) p = "Hostname"; else if ( !k ) if ( has_pow(w, d->player, POW_HOST) ) { p = d->addr; #ifdef BRACKET_HOST if ( who_sizes[i] > who_sizes_small[i] ) { who_bh_buf[0] = '['; for ( j = 1, p = d->addr; j <= 30 && *p != '\0'; j++, p++ ) who_bh_buf[j] = *p; who_bh_buf[j++] = ']'; who_bh_buf[j] = '\0'; p = who_bh_buf; } #endif /* BRACKET_HOST */ } break; default: continue; } sprintf(b, who_fmt[i], p); b += who_sizes[i] + 2; } b -= 2; if ( header ) --hc; if ( (!header && ++grp <= num_grps) || (header && hc) ) { /* make space between groups */ strcpy(b, " "); b += 4; } else { /* trim excess spaces */ while ( *--b == ' ' ) ; /* print output line */ if ( k ) { *++b = '\n'; *++b = '\0'; queue_string(k, buf); } else { *++b = '\0'; notify(w, buf); } /* reset for new line */ grp = 1; b = buf; } if ( header ) { if ( grp == 1 && hc <= 0 ) hc = header = 0; } else d = d->next; } } /* print last line (if incomplete) */ if ( grp > 1 ) { /* trim extra spaces */ while ( *--b == ' ' ) ; if ( k ) { *++b = '\n'; *++b = '\0'; queue_string(k, buf); } else { *++b = '\0'; notify(w, buf); } } b = buf; if ( hidden_count > 0 ) { sprintf(b, "Hidden Users%s: %d ", ( name_list[0] > 0 ) ? " Found" : "", hidden_count); b = b + strlen(b); } sprintf(b, "Total Users%s: %d.", ( name_list[0] > 0 ) ? " Found" : "", total_count); if ( k ) { queue_string(k, "---\n"); queue_string(k, buf); queue_string(k, "\n"); } else { notify(w, "---"); notify(w, buf); } } char *time_format_1(dt) time_t dt; { struct tm *delta; static char buf[64]; if(dt < 0) dt = 0; delta = gmtime(&dt); if (delta->tm_yday > 0) { sprintf(buf, "%dd %02d:%02d", delta->tm_yday, delta->tm_hour, delta->tm_min); } else { sprintf(buf, "%02d:%02d", delta->tm_hour, delta->tm_min); } return buf; } char *time_format_2(dt) time_t dt; { register struct tm *delta; static char buf[64]; if(dt < 0) dt = 0; delta = gmtime(&dt); if (delta->tm_yday > 0) { sprintf(buf, "%dd", delta->tm_yday); } else if (delta->tm_hour > 0) { sprintf(buf, "%dh", delta->tm_hour); } else if (delta->tm_min > 0) { sprintf(buf, "%dm", delta->tm_min); } else { sprintf(buf, "%ds", delta->tm_sec); } return buf; } void announce_connect(player) dbref player; { dbref loc; char buf[BUFFER_LEN]; extern dbref speaker; char *s, t[30]; /* we put this here instead of in connect_player cuz we gotta */ time_t tt; /* notify the player and the descriptor isn't set up yet then */ int connect_again = db[player].flags&PLAYER_CONNECT; #ifdef USE_RWHO time_t tv; rwhocli_userlogin(tprintf("%d@%s",player,RWHO_MUSE_NAME),db[player].name,tv); #endif if ((loc = getloc(player)) == NOTHING) return; if (connect_again) sprintf(buf, "%s has reconnected.", db[player].name); else sprintf(buf, "%s has connected.", db[player].name); /* added to allow player's inventory to hear a player connect */ speaker=player; notify_in(player, player, buf); if (!IS(loc,TYPE_ROOM,ROOM_AUDITORIUM)) notify_in(loc, player, buf); db[player].flags|=PLAYER_CONNECT; db[player].flags&=~HAVEN; if (!Guest(player)) { time(&tt); strcpy(t,ctime(&tt)); t[strlen(t)-1]=0; tt=atol(atr_get(player,A_LAST)); if ( tt == 0L ) s = "no previous login"; else { s = ctime(&tt); s[strlen(s)-1]=0; if ((strncmp(t, s, 10) != 0) && power(player, POW_MEMBER) && db[player].owner == player) { giveto(player, PAY_CHECK); notify(player, tprintf("You collect %d credits.", PAY_CHECK)); } } notify(player,tprintf("Last login: %s", s)); time(&tt); sprintf(buf, "%ld", tt); atr_add(player,A_LAST,buf); check_mail(player); } if (!connect_again) { dbref thing; dbref zone; dbref who=player; int depth; int find=0; did_it(who, who, NULL, NULL, A_OCONN, NULL, A_ACONN); did_it(who, db[who].location, NULL, NULL, NULL, NULL, A_ACONN); zone=db[0].zone; if (Typeof(db[who].location) == TYPE_ROOM) { zone=db[db[who].location].zone; } else { thing = db[who].location; for (depth = 10; depth && (find != 1); depth--, thing = db[thing].location) if (Typeof(thing) == TYPE_ROOM) { zone=db[thing].zone; find=1; } } if ((db[0].zone != zone) && (Typeof(db[0].zone) != TYPE_PLAYER)) did_it(who, db[0].zone, NULL, NULL, NULL, NULL, A_ACONN); if (Typeof(zone) != TYPE_PLAYER) did_it(who, zone, NULL, NULL, NULL, NULL, A_ACONN); if ((thing = db[who].contents) != NOTHING) { DOLIST(thing,thing) { if(Typeof(thing) != TYPE_PLAYER) { did_it(who, thing, NULL, NULL, NULL, NULL, A_ACONN); } } } if ((thing = db[db[who].location].contents) != NOTHING) { DOLIST(thing,thing) { if(Typeof(thing) != TYPE_PLAYER) { did_it(who, thing, NULL, NULL, NULL, NULL, A_ACONN); } } } } } void announce_disconnect(player) dbref player; { dbref loc; int num; char buf[BUFFER_LEN]; struct descriptor_data *d; extern dbref speaker; int partial_disconnect; if(player<0) return; for(num=0,d=descriptor_list;d;d=d->next) if (d->state==CONNECTED && (d->player>0) && (d->player==player)) num++; if (num<2 && !shutdown_flag) { db[player].flags&=~PLAYER_CONNECT; atr_add(player,A_IT,""); partial_disconnect = 0; } else partial_disconnect = 1; #ifdef USE_RWHO rwhocli_userlogout(tprintf("%d@%s",player,RWHO_MUSE_NAME)); #endif if ((loc = getloc(player)) != NOTHING) { if (partial_disconnect) sprintf(buf, "%s has partially disconnected.", db[player].name); else sprintf(buf, "%s has disconnected.", db[player].name); speaker=player; /* added to allow player's inventory to hear a player connect */ notify_in(player, player, buf); if(!IS(loc,TYPE_ROOM,ROOM_AUDITORIUM)) notify_in(loc, player, buf); if (!partial_disconnect) { dbref zone; dbref thing; dbref who=player; int depth; int find=0; did_it(who, who, NULL, NULL, A_ODISC, NULL, A_ADISC); did_it(who, db[who].location, NULL, NULL, NULL, NULL, A_ADISC); zone=db[0].zone; if (Typeof(db[who].location) == TYPE_ROOM) { zone=db[db[who].location].zone; } else { thing = db[who].location; for (depth = 10; depth && (find != 1); depth--, thing = db[thing].location) if (Typeof(thing) == TYPE_ROOM) { zone=db[thing].zone; find=1; } } if ((db[0].zone != zone) && (Typeof(db[0].zone) != TYPE_PLAYER)) did_it(who, db[0].zone, NULL, NULL, NULL, NULL, A_ADISC); if (Typeof(zone) != TYPE_PLAYER) did_it(who, zone, NULL, NULL, NULL, NULL, A_ADISC); if ((thing = db[who].contents) != NOTHING) { DOLIST(thing,thing) { if(Typeof(thing) != TYPE_PLAYER) { did_it(who, thing, NULL, NULL, NULL, NULL, A_ADISC); } } } if ((thing = db[db[who].location].contents) != NOTHING) { DOLIST(thing,thing) { if(Typeof(thing) != TYPE_PLAYER) { did_it(who, thing, NULL, NULL, NULL, NULL, A_ADISC); } } } } } } #if defined(HPUX) || defined(SYSV) #include <unistd.h> int getdtablesize() { return (int) sysconf(_SC_OPEN_MAX); } #endif #ifdef SYSV void setlinebuf(x) FILE *x; { setbuf(x,NULL); } int vfork() { return fork(); } #endif struct ctrace_int { struct descriptor_data *des; struct ctrace_int **children; }; static struct ctrace_int *internal_ctrace(parent) struct descriptor_data *parent; { struct descriptor_data *k; struct ctrace_int *op; extern NALLOC *glurp; int nchild; op=na_alloc(glurp,sizeof(struct ctrace_int)); op->des=parent; if(parent && !(parent->cstatus&C_CCONTROL)) { op->children=na_alloc(glurp,sizeof(struct ctrace_int *)); op->children[0]=0; } else { for(nchild=0,k=descriptor_list;k;k=k->next) if(k->parent == parent) nchild++; op->children=na_alloc(glurp,sizeof(struct ctrace_int *)*(nchild+1)); for(nchild=0,k=descriptor_list;k;k=k->next) if(k->parent==parent) { op->children[nchild]=internal_ctrace(k); nchild++; } op->children[nchild]=0; } return op; } static void ctrace_notify_internal(player,d,dep) dbref player; struct ctrace_int *d; int dep; { char buf[2000]; int j,k; for(j=0;j<dep;j++) buf[j]='.'; if(d->des && dep) { sprintf(buf+j,"%s descriptor: %d, concid: %d, host: %s", (d->des->state==CONNECTED) ?(tprintf("\"%s\"",unparse_object(player,d->des->player))) :((d->des->cstatus&C_CCONTROL) ?"<Concentrator Control>" :"<Unconnected>"), d->des->descriptor, d->des->concid, d->des->addr); notify(player,buf); } for(k=0;d->children[k];k++) ctrace_notify_internal(player,d->children[k],dep+1); } void do_ctrace(player) dbref player; { struct ctrace_int *dscs; if(!power(player,POW_HOST)) { notify(player,"Sorry."); return; } dscs=internal_ctrace(0); ctrace_notify_internal(player,dscs,0); } #ifdef RESOCK void resock() { log_io("resocking..."); close(sock); sock = make_socket(TINYPORT); log_io("resocking done"); } #endif