/* This software is copyrighted by the University of Pennsylvania. Read COPYRIGHT for details. */ #include #include #include #include #include #include #include #include #include #include #include "network.h" #include "pdb.h" #include "node.h" #include "gophernodes.h" extern int addednewnodes; extern int tables_changed; extern short todaysdate; extern pid_t savedatastruct_pid; extern struct s1 *getnode_bygopherinfo(char **fields); extern struct s1 *init_gopher_node (char **fields, long nid, short lastu, int chkexists); extern void nio_send_file(int s, char *fn, long stpt, long req, int isnlist); extern void send_empty_document (int s); extern void send_empty_menu (int s); extern char *nodeinfostr(long nid, struct s1 *node, int flgfmt); extern CONN conntab[]; #define conn_established(cn) (conntab[cn].c_socket != -1) extern void remove_helper(WAITFORHELPER *ptr) { int i, found; if (ptr == NULL) return; kill (ptr->pid, SIGTERM); /* just in case child is out there */ do_free (ptr->file); do_free (ptr->cachefile); /* DONT free the ptr->node, it's part of the datastruct */ for (i=0, found = 0; !found && i < FD_SETSIZE; ) { if (conntab[i].c_hptr == ptr) found = 1; else i++; } free (ptr); if (found) conntab[i].c_hptr = NULL; return; } int next_id; /* increments next id in memory + on disk */ /* assumes that get_nextid() has been called already */ static void inc_nextid(void) { char line[20]; int dfd; if ((dfd = open(NEXTID_FILE, O_WRONLY | O_TRUNC | O_CREAT)) <= 0) { printf("error incrementing next id\n"); return; } next_id++; sprintf(line, "%d\n", next_id); write(dfd, line, strlen(line)); close(dfd); return; } /* reads the next id from disk */ extern void get_nextid(void) { char line[20]; int red; int dfd; if ((dfd = open(NEXTID_FILE, O_RDONLY)) <= 0){ printf("error getting next id\n"); return; } red = read(dfd, line, 20); if (red <= 0){ printf("error reading next id\n"); return; } line[red] = '\0'; next_id = atoi(line); close(dfd); return; } static void conv_textfile(FILE *ifile, char *filnam) { FILE *ofile; char line[NLINE_MAXLEN]; char *cp; ofile = fopen(filnam, "w"); if (ofile == NULL) { fprintf (stderr, "Unable to open temp nlist file\n"); perror(filnam); return; } while (fgets (line,NLINE_MAXLEN-1,ifile) != NULL) { cp = rindex (line, '\r'); if (cp) if (*(cp+1) == '\n') { *cp = '\n'; *(cp+1) = 0; } /* gopher protocol: text file ends with a . on a line by itself */ if (strcmp(line, ".\n") == 0) break; else fprintf (ofile, "%s", line); } fclose (ofile); } static void child_punt (char gophertype, long nodeid, int sock, int flgsfmt) { char *cp; if (gophertype == GOPHTYP_TEXT || gophertype == GOPHTYP_GIF || gophertype == GOPHTYP_IMAGE) { send_empty_document(sock); } else if (gophertype == GOPHTYP_MENU) { /* send this- 2: 0:nodeinfo of node we were working on 1:nodeinfo of reserved node that says "menu not available" */ cp = (char *) malloc (2*BUFSIZ); sprintf (cp, "2:\n0:%s\n", nodeinfostr(nodeid, NULL, flgsfmt)); strcat (cp, "1:"); strcat (cp, nodeinfostr(MENU_NOT_AVAILABLE, NULL, flgsfmt)); strcat (cp, "\n.\r\n"); sendbuf(cp, strlen(cp), sock); /* sendbuf will free cp */ } else { send_empty_menu (sock); } } static int create_cached_nlist (FILE *ifile, char *tempname) { FILE *tempfile; char line[BUFSIZ]; char *gophfields[NUMFIELDS_GOPHER]; int numf; struct s1 *node; int numlines; tempfile = fopen (tempname, "w"); if (tempfile == NULL) { fprintf (stderr, "Unable to write temp output nlist file\n"); perror (tempname); return 0; } /* ifile is an already open FILE */ for (rewind (ifile), numlines = 0; fgets(line, sizeof(line)-1, ifile) != NULL; ) { if (*line != GOPHTYP_DUPSRV) { parsefields (line, gophfields, &numf, GOPHER_DLM, NUMFIELDS_GOPHER); if (numf >= NUMFIELDS_GOPHER) { node = getnode_bygopherinfo(gophfields); if (node == NULL) { /* create a new node in tables */ get_nextid(); node = init_gopher_node (gophfields, next_id, todaysdate, 0); inc_nextid(); addednewnodes++; tables_changed++; } /* gopher info is already known, therefore just use the existing nodeid */ fprintf (tempfile, "%ld\n", node->nodeid); numlines++; } /* enough gopher fields */ } /* it wasn't a dupl server line */ } /* foreach line of ifile */ fclose (tempfile); return 1; } static int find_helper_conn (pid_t pid) { int found; int con; for (found = 0, con = 0; con < FD_SETSIZE && !found;) { if (conn_established(con) && (conntab[con].c_hptr) && (conntab[con].c_hptr)->pid == pid) { found = 1; } else con++; } if (!found) return -1; else return con; } extern void catch_child (void) { pid_t pid; union wait status; struct rusage rusage; int c, found; FILE *childfile; WAITFORHELPER *hptr; char *filename2; sigset_t currmask; pid = wait3 (&status, WNOHANG, &rusage); if (pid == 0) { fprintf (stderr, "Funny, wait3 returned pid 0!\n"); return; /* don't know if there's a connect out there waiting */ } else if (pid < 0) { fprintf (stderr, "wait3 returned %d\n", pid); perror ("wait3"); return; /* don't know if there's a connect out there waiting */ } /* (pid > 0) */ fprintf (stderr, "\nT=%d. Caught pid %d, exitstat = %d. ", time(0), pid, WEXITSTATUS(status)); if ((c=find_helper_conn (pid)) < 0) { if (pid != savedatastruct_pid) fprintf (stderr, "Unknown what connection %d went with\n", pid); else { savedatastruct_pid = 0; fprintf(stderr, "save_datastruct child.\n"); } return; } else fprintf (stderr, "Goes with conn #%d, sock %d\n", c, conntab[c].c_socket); hptr = conntab[c].c_hptr; /* Assumption: c_hptr->node still points to a valid node in a1[] */ /* if child exited with non-zero status, must be an error */ if (WEXITSTATUS(status) != 0) { child_punt((hptr->node)->gophertype, (hptr->node)->nodeid, conntab[c].c_socket, conntab[c].c_output_fmt); unlink (hptr->file); return; } childfile = fopen (hptr->file,"r"); if (childfile == NULL) { /* File wasn't there ??? */ fprintf (stderr, "Could not open child's output file\n"); perror (hptr->file); child_punt ((hptr->node)->gophertype, (hptr->node)->nodeid, conntab[c].c_socket, conntab[c].c_output_fmt); return; } else { filename2 = tempnam(CACHEDIR, "convert"); /* has to be the same dir as the cache because rename() needs that */ switch ((hptr->node)->gophertype) { case GOPHTYP_IMAGE: case GOPHTYP_GIF: /* No conversion is needed for GIF files */ fclose (childfile); rename (hptr->file, hptr->cachefile); nio_send_file(conntab[c].c_socket, hptr->cachefile, hptr->startpoint, hptr->requested, 0); remove_helper(hptr); do_free(filename2); break; case GOPHTYP_TEXT: conv_textfile (childfile, filename2); rename (filename2, hptr->cachefile); nio_send_file(conntab[c].c_socket, hptr->cachefile, hptr->startpoint, hptr->requested, 0); fclose (childfile); /* conv_... did NOT close file */ unlink (hptr->file); /* remove child's file */ remove_helper(hptr); do_free (filename2); break; case GOPHTYP_MENU: case GOPHTYP_SEARCH: if (!create_cached_nlist (childfile, filename2)) { child_punt((hptr->node)->gophertype, (hptr->node)->nodeid, conntab[c].c_socket, conntab[c].c_output_fmt); } else { rename (filename2, hptr->cachefile); send_cached_nlist (hptr->node, conntab[c].c_socket, hptr->cachefile, conntab[c].c_output_fmt); } fclose (childfile); unlink (hptr->file); /* remove child's file from the filesystem */ remove_helper(hptr); /* remove the buf associated with child */ do_free(filename2); break; default: fprintf (stderr, "catch_child() doesn't handle type '%c'.", (hptr->node)->gophertype); child_punt ((hptr->node)->gophertype, (hptr->node)->nodeid, conntab[c].c_socket, conntab[c].c_output_fmt); fclose (childfile); /* close file */ unlink (hptr->file); /* remove child's file */ remove_helper(hptr); do_free (filename2); break; } } /* childfile was okay */ }