|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T n
Length: 23526 (0x5be6) Types: TextFile Names: »nntplink.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit └─⟦f91e15335⟧ »EurOpenD3/news/nntp/nntplink2.0.0.tar.Z« └─⟦2c70c5e6b⟧ └─⟦this⟧ »nntplink.c«
/** nntplink - transmit netnews articles across the internet with nntp, ** holding the link open semi-permanently. ** ** Derived from nntpxmit.c (by Erik Fair) by Warren Lavallee. ** ** Copyright (c) 1989 Samsung Software America, Inc. All rights reserved. ** ** Permission is hereby granted to copy, reproduce, redistribute or ** otherwise use this software as long as: (1) there is no monetary ** profit gained specifically from the use or reproduction of this ** software, (2) it is not sold, rented, traded, or otherwise marketed, ** (3) the above copyright notice and this paragraph is included ** prominently in an copy made, and (4) that the name of Samsung ** is not used to endorse or promote products derived from this software ** without the specific prior written permission of Samsung Software America. ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED ** WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** ** Version history: ** 1.01.0 - 11-02-89 - SVR3 compatible? ** 1.01.1 - 11-03-89 - Cnews batchfile compatibility (coolidge@cs.uiuc.edu) ** 1.01.2 - 11-05-89 - Removed -x option. It is always set now. ** Very small change to safecopy(). ** 1.01.3 - 11-08-89 - Fix dprintf bug (big bug) <fletcher@cs.utexas.edu> ** 1.01.4 - 11-10-89 - Added HP/UX support. <icsu6000@cs.montana.edu> ** 1.10.0 - 11-11-89 - Initial public release ** 1.10.2 - 11-15-89 - Fixes major bug (dropped articles)--- ** linked lists removed. ** - 11-19-89 - Added -L and -C options, reorganized stats ** gathering. <lamy@ai.utoronto.ca> ** 1.10.3 - 11-20-89 - Removed one-shot feature-- doesn't apply. ** Added -S option <coolidge@cs.uiuc.edu> ** 1.10.4 - 11-23-89 - Added -I (idling time out) <lamy@ai.utoronto.ca> ** 1.10.5 - 11-27-89 - Split up nntplink.c to several smaller files. ** Message-id is now save in rewrite() ** Code to check C-news .vs. B-news (spots cfg ** errors) ** ** Contributors/Testers: ** karl@tut.cis.ohio-state.edu (Karl Kleinpaste) ** fletcher@cs.utexas.edu (Fletcher Mattox) ** mcooper@usc.edu (Micheal Cooper) ** alden@gem.mps.ohio-state.edu (Dave Alden) ** icsu6000@caesar.cs.montana.edu (Mathisen) ** lamy@ai.utoronto.ca (Jean-Francois Lamy) ** coolidge@cs.uiuc.edu (John Coolidge) ** ** Testers: ** dubois@uakari.primate.wisc.edu (Paul Dubois) ** coolidge@casca.cs.uiuc.edu (John Coolidge) ** lloyd@aplcen.jhu.edu (Lloyd W. Taylor) ** tale@pawl.rpi.edu (David C Lawrence) ** mb@rex.cs.tulane.edu (Mark Benard) ** asp@uunet.uu.net (Andrew Partan) ** ** OPERATING SYSTEMS: ** Pyramid DualPort OSx4.4c ** Dynix 3.0 (Symmetry + Balance) ** HP-UX 6.01 ** Sun OS {4.0,3.5} ** Ultrix 3.? ** 4.3tahoe (uVAX III) ** **/ #include "conf.h" #include <stdio.h> #include <errno.h> #include <ctype.h> #include <sys/types.h> #ifdef BSD4_2 #include <sys/time.h> #include <sys/resource.h> #include <sys/ioctl.h> #include <sys/file.h> #else #include <sys/times.h> extern time_t time(); #endif BSD4_2 #include <fcntl.h> #include <signal.h> #ifdef USG #include "sysexits.h" #else #include <sysexits.h> #endif #ifdef SYSLOG #include <syslog.h> #endif SYSLOG #include "nntp.h" #ifdef BSD4_2 #include <sys/wait.h> #endif #include <sys/stat.h> #define MAXFNAME BUFSIZ /* maximum filename size - big enough? */ #define FCLOSE(fp) (void) fclose(fp); (fp) = (FILE *)NULL FILE *getfp(); char *errmsg(); void requeue(); void catchsig(); void logstats(); void log(); void usage(); int interrupted(); void waitingfor(); /* ** Globals that certain things need. ** ** Various subroutines want the program name to report errors. ** The queue file, queue file pointer and current article name are ** there to write out the state of the queue file from a signal handler ** (that is, the list of unsent and (possibly) failed articles) so ** that when next we try sending to a given remote site, we don't send ** stuff we've already sent. */ char *Pname; /* this program's invocation name */ char *Host; /* current remote host */ char *TQfile; /* BOGUS HACK - BETA RELEASE ONLY */ char Qfile[MAXFNAME]; /* current queue file we're operating on */ FILE *Qfp; /* the (FILE *) for above */ char Article[MAXFNAME]; /* current article filename */ char FailedArticle[MAXFNAME];/* article to requeue */ int resetstats = TRUE; /* true after a logstats() */ unsigned short connected = 0; char nntpfile[MAXFNAME]; #ifdef CNEWS int cnews = 1; #else int cnews = 0; #endif char *safecopy(); int Use_Log_File = FALSE; int In_Log_File = FALSE; char logline[MAXLOGLENGTH] = ""; char old_logline[MAXLOGLENGTH] = ""; char orig_Qfile[MAXFNAME]; char orig_nntpfile[MAXFNAME]; char sysname[MAXSYSNAME]; /* ** Some flags, toggled by arguments */ #define TOGGLE(boolean) (boolean) = !(boolean) char Debug = FALSE; char Report_Stats = TRUE; char ReQueue_Fails = TRUE; /* ** Numeric arguments */ int Close_After=CLOSE_AFTER; /* close connection to remote at next pause if we've offered this many articles since last pause */ int Log_After = LOG_AFTER; /* log stats at next pause if we've proposed this many aritcles since last pause */ int Sleep_Time = SLEEP_INT; /* Sleep Sleep_Time seconds between checks of the queue file. */ char CheckIdleTimeout = FALSE; long IdleTimeOut; /* if we've been waiting for batch file for longer than this many seconds, close link while waiting */ char CheckExitTimeout = FALSE; long ExitTimeOut; char *USAGE = "usage: nntplink [-d][-s][-r][-T][-F][-D][-Sn][-Cn][-Ln][-In][-En][-Xsysname] hostname|hostname:file [...]"; char *Fmt = "%s localhost %s[%d]: %s\n"; char *E_fopen = "fopen(%s, \"%s\"): %s"; char *E_unlk = "unlink(%s): %s"; #ifdef USELOG char *NNTPlog = USELOG; /* yet another external log file */ FILE *Logfp = (FILE *)NULL; int fd; #endif USELOG struct { u_long offered; u_long accepted; u_long rejected; u_long failed; u_long since_close; } Stats = {0L, 0L, 0L, 0L, 0L}; double Tbegin, Tend; /* transfer timestamps */ double ouser = 0.0, osys = 0.0; double user, sys; extern int errno; extern int strncmp(); extern char *rindex(); extern char *index(); extern char *mktemp(); extern char *strcpy(); #ifdef USG void bzero(s, l) register caddr_t s; register int l; { while(l-- > 0) *s++ = 0; } #endif USG extern char *compiled, *version; /* ** Former parts of main(), now externals. */ int i; int transport = T_IP_TCP; /* default is IP/TCP */ #ifdef USELOG char *amode = "a"; #endif USELOG #ifdef BSD4_2 struct timeval tod; struct timezone tz; #endif BSD4_2 main(ac, av) int ac; char *av[]; { #ifdef BSD4_2 (void) gettimeofday(&tod, &tz); Tend = Tbegin = tod.tv_sec + (double)tod.tv_usec/1000000.; #else Tend = Tbegin = (double) time((time_t *)NULL); #endif BSD4_2 #ifdef BATCH_DIR chdir (BATCH_DIR); #endif Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]); if (ac < 2) { usage(); } #ifdef SYSLOG /* 4.2 BSD openlog has only two args */ #ifdef LOG_LOCAL7 (void) openlog(Pname, LOG_PID, LOG_LINK); #else (void) openlog(Pname, LOG_PID); #endif LOG_LOCAL_7 #endif SYSLOG #ifdef USELOG fd = open(NNTPlog,O_APPEND|O_CREAT|O_WRONLY); if ((Logfp = fdopen(fd, amode)) == (FILE *)NULL) { char buf[BUFSIZ]; sprintf(buf, E_fopen, NNTPlog, amode, errmsg(errno)); log(L_NOTICE, buf); } #endif USELOG for(i = 1; i < ac; i++) { if (av[i][0] == '-') { switch(av[i][1]) { case 'T': transport = T_IP_TCP; break; case 'D': transport = T_DECNET; break; case 'F': transport = T_FD; break; case 's': TOGGLE(Report_Stats); break; case 'd': TOGGLE(Debug); break; case 'r': TOGGLE(ReQueue_Fails); break; case 'C': Close_After = atoi(av[i]+2); break; case 'L': Log_After = atoi(av[i]+2); break; case 'S': Sleep_Time = atoi(av[i]+2); break; case 'I': CheckIdleTimeout = TRUE; IdleTimeOut = atoi(av[i]+2); break; case 'E': CheckExitTimeout = TRUE; ExitTimeOut = atoi(av[i]+2); break; case 'X': strcpy(sysname, " "); /* We want to make sure that we */ strcat(sysname, av[i]+2); /* don't match part of another */ strcat(sysname, " "); /* sysname, so we pad with spaces */ Use_Log_File = TRUE; break; case 'v': default: fprintf(stderr, "%s: no such option: -%c\n", Pname, av[i][1]); usage(); } continue; } /* ** OK, it wasn't an option, therefore it must be a ** hostname, filename pair. ** ** If the user typed host::file, then it's DECNET, ** whether they remembered the "-D" option or not. */ Host = av[i]; if ((TQfile = index(Host, ':')) != (char *)NULL) { if (TQfile[1] == ':') { transport = T_DECNET; *TQfile++ = '\0'; } else if (transport != T_FD) transport = T_IP_TCP; *TQfile++ = '\0'; } else TQfile = Host; strcpy(Qfile, TQfile); strcpy(orig_Qfile, TQfile); /* * If we're not debugging, then we should fade into the * background completely, disconnecting from the terminal, * and shutting off signals. */ if (!Debug) { #ifdef TIOCNOTTY int fd; #endif #ifdef AUTOBACKGROUND int p; if ((p = fork()) < 0) /* ah-oo-gah! Dive, dive! */ { (void) fprintf(stderr, "%s: fork failed\n", Pname); exit(1); } if (p != 0) exit(0); /* parent exit, child continues */ (void) signal(SIGHUP, SIG_IGN); /* shut down signals */ (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_IGN); #endif /* SIGTSTP */ #endif #ifdef TIOCNOTTY fd = open("/dev/tty", 2); /* disconnect from the tty */ if (fd >= 0) { (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0); (void) close(fd); } #endif /* TIOCNOTTY */ (void) setpgrp(0, 0); } else { fprintf(stderr, "%s\n", version+4); fprintf(stderr, "%s\n", compiled+4); fprintf(stderr, "\nConfiguration info:\n\n"); fprintf(stderr, "Host: %s Queue file: %s\n", Host, Qfile); fprintf(stderr, "Connection type: "); switch (transport) { case T_IP_TCP: fprintf(stderr, "TCP/IP\n"); break; case T_DECNET: fprintf(stderr, "DECNET\n"); break; case T_FD: fprintf(stderr, "FD\n"); break; default: fprintf(stderr, "Unknown\n"); break; } if (Report_Stats) fprintf(stderr, "Statistics will %s.\n", #ifdef USELOG "be reported via UNIX file" #else #ifdef LOG_LINK "be reported via 4.3BSD syslog" #else "not be reported" #endif #endif ); if (ReQueue_Fails) fprintf(stderr, "Failed articles will be requeued.\n"); else fprintf(stderr, "Failed articles will NOT be requeued.\n"); if (Close_After) { fprintf(stderr, "Connection will be closed after %d articles have been offered\n", Close_After); } if (Log_After) fprintf(stderr, "Will log stats locally after %d articles have been processed.\n", Log_After); else fprintf(stderr, "Will log xfer stats after each queue file is finished.\n"); if (Sleep_Time) fprintf(stderr, "Will sleep %d second%s between checks for queue file.\n", Sleep_Time, (Sleep_Time == 1) ? "" : "s"); if (CheckIdleTimeout) fprintf(stderr, "Will close the connection when idle for more than %d seconds.\n", IdleTimeOut); if (CheckExitTimeout) fprintf(stderr, "Will exit when idle for more than %d seconds.\n", ExitTimeOut); fprintf(stderr, "\n"); fflush(stderr); } bzero((caddr_t)&Stats, sizeof(Stats)); #ifndef DONT_WATCH { register ret; int pid; retry: ret = fork(); if (!ret) /** child startup **/ { sleep(2); /* allow some time for debugging msgs to flush */ #endif while(1) { dprintf(stderr, "Looping forever\n"); think(); } #ifndef DONT_WATCH } if (ret == -1) /** fork failed **/ { sleep(15); /* back off */ goto retry; } dprintf(stderr, "Waiting for child process to die.\n"); pid = wait(0); fprintf(stderr, "child process no longer alive.\n"); goto retry; } #endif } exit(EX_OK); } void usage() { (void) fprintf(stderr, "%s: %s\n", Pname, USAGE); exit(EX_USAGE); } think () { int naptime = NAP_TIME; catchsig(interrupted); for (;;) { if (!sendnews(Host, transport, safecopy(Qfile))) { dprintf(stderr, "sendnews returned an error... sleeping for %d seconds...\n", naptime); sleep(naptime); if (naptime < 600) naptime *= 2; } else naptime = NAP_TIME; dprintf(stderr, "Back from Sendnews. %s\n", connected ? "Still Connected." : "Not connected"); } } char * errmsg(code) int code; { extern int sys_nerr; extern char *sys_errlist[]; static char ebuf[6+5+1]; if (code > sys_nerr || code < 0) { (void) sprintf(ebuf, "Error %d", code); return ebuf; } else return sys_errlist[code]; } /* ** strip leading and trailing spaces */ char * sp_strip(s) register char *s; { register char *cp; if (s == NULL) return(NULL); if (*s == '\0') return(s); cp = &s[strlen(s) - 1]; while(cp > s && isspace(*cp)) cp--; *++cp = '\0'; /* zap trailing spaces */ for(cp = s; *cp && isspace(*cp); cp++) continue; return(cp); /* return pointer to first non-space */ } /* ** convert `s' to lower case */ char * lcase(s) register char *s; { register char *cp; if (s == (char *)NULL) return(s); for(cp = s; *cp != '\0'; cp++) if (isupper(*cp)) *cp = tolower(*cp); return(s); } /* ** Get the message-id header field data with a minimum of fuss. */ char * getmsgid(fp) FILE *fp; { static char buf[BUFSIZ]; static char *msgid = "message-id"; register char *cp, *cp2; while(fgets(buf, sizeof(buf), fp) != (char *)NULL) { switch(buf[0]) { case '\n': return((char *)NULL); /* EOH, we failed */ case 'M': case 'm': if ((cp = index(buf, ':')) == (char *)NULL) continue; *cp++ = '\0'; if (strncmp(lcase(buf), msgid, sizeof(*msgid)) == 0) { /* dump extraneous trash - umass.bitnet */ /* hope nobody quotes an '>' in a msgid */ if ((cp2 = index(cp, '>')) != (char *)NULL) *++cp2 = '\0'; return(sp_strip(cp)); } break; } } return((char *)NULL); /* EOF, we failed */ } #ifdef notdef /* nobody obeys the triply damned protocol anyway! */ /* ** Special characters, see RFC822, appendix D. */ isspecial(c) char c; { char *specials = "()<>@,;:\\\".[]"; return(index(specials, c) != (char *)NULL ? TRUE : FALSE); } /* ** Check on the validity of an RFC822 message-id ** ** By The Book, RFC822 Appendix D. ** msg-id = "<" addr-spec ">" ** addr-spec = local-part "@" domain ** local-part = word *("." word) ** word = atom / quoted-string ** domain = sub-domain *("." sub-domain) ** sub-domain = domain-ref / domain-literal ** domain-ref = atom ** domain-literal = "[" *(dtext / quoted-pair) "]" ** ** NOTE: close reading of the RFC822 spec indicates that a fully ** qualified domain name (i.e. one with at least one dot) is ** NOT required in the domain part of the addr-spec. However, ** I've decided to be an asshole and require them, since we'll ** all die a slow death later on if I don't at this juncture. ** To disable, if you disagree with me, see the last return ** statement. - Erik E. Fair <fair@ucbarpa.berkeley.edu> ** May 30, 1986 */ msgid_ok(id) register char *id; { register Langle = FALSE; register Rangle = FALSE; register local_part = FALSE; register at = FALSE; register dot = FALSE; /* skip up to the opening angle bracket */ if (id == (char *)NULL || (id = index(id, '<')) == (char *)NULL) return(FALSE); /* don't waste my time! */ for(; *id != '\0'; id++) { switch(*id) { case '<': if (Langle) return(FALSE); Langle = local_part = TRUE; break; case '>': if (Rangle || !Langle || !at) return(FALSE); else Rangle = TRUE; break; case '@': /* should be a domain spec */ at = TRUE; local_part = FALSE; break; case '.': dot = at; break; case '\\': /* ** quoted pair; this disallows NULs, but how ** many mailers would die if someone used one? */ if (!local_part || (*++id) == '\0') return(FALSE); break; case '"': /* ** quoted string */ if (!local_part) return(FALSE); do { switch(*++id) { case '\\': if ((*++id) == '\0') return(FALSE); break; case '\r': return(FALSE); } } while(*id != '\0' && *id != '"'); break; case '[': /* ** domain literal */ if (local_part) return(FALSE); do { switch(*++id) { case '\\': if ((*++id) == '\0') return(FALSE); break; case '\r': return(FALSE); } } while(*id != '\0' && *id != ']'); break; default: if (!isascii(*id) || iscntrl(*id) || isspace(*id) || isspecial(*id)) return(FALSE); /* quit immediately */ break; } } return(at && dot && Langle && Rangle); } #else notdef /* ** Simpleton's check for message ID syntax. ** A concession to the realities of the ARPA Internet. */ msgid_ok(s) register char *s; { register char c; register in_msgid = FALSE; if (s == (char *)NULL) return(FALSE); while((c = *s++) != '\0') { if (!isascii(c) || iscntrl(c) || isspace(c)) return(FALSE); switch(c) { case '<': in_msgid = TRUE; break; case '>': return(in_msgid); } } return(FALSE); } #endif notdef /* ** Read the header of a netnews article, snatch the message-id therefrom, ** and ask the remote if they have that one already. */ ihave(fp, mesgid) FILE *fp; char *mesgid; { register int code; register char *id; char buf[BUFSIZ]; dprintf(stderr,"%s: entering ihave\n", Pname);fflush(stderr); if (strlen(mesgid) > 0) { id = mesgid; } else { if ((id = getmsgid(fp)) == (char *)NULL || *id == '\0') { /* ** something botched locally with the article ** so we don't send it, but we don't break off ** communications with the remote either. */ sprintf(buf, "%s: message-id missing!", Article); log(L_DEBUG, buf); return(ERR_GOTIT); } strcpy(mesgid,id); } if (!msgid_ok(id)) { sprintf(buf, "%s: message-id syntax error: %s", Article, id); log(L_DEBUG, buf); return(ERR_GOTIT); } sprintf(buf, "IHAVE %s", id); Stats.offered++; Stats.since_close++; switch(code = converse(buf, sizeof(buf))) { case CONT_XFER: Stats.accepted++; rewind(fp); return(code); case ERR_GOTIT: Stats.rejected++; return(code); default: strcpy(mesgid,id); return(code); } } /* ** Given that fp points to an open file containing filenames, ** open and return a file pointer to the next filename in the file. ** Don't you love indirection? ** ** Returns a valid FILE pointer or NULL if end of file. */ FILE * getfp(fp, filename, fnlen, mesgid, host) register FILE *fp; char *filename; register int fnlen; char *mesgid; char *host; { register FILE *newfp = (FILE *)NULL; register char *cp; char *mode = "r"; char buffer[255]; char shortfn[255]; char *p; if (Stats.offered >= Log_After && Report_Stats) { logstats(); resetstats = FALSE; dprintf(stderr, "resetting timers\n"); Tbegin = Tend; ouser = user; osys = sys; Stats.offered = Stats.accepted = Stats.rejected = Stats.failed = 0L; } if ((Stats.since_close >= Close_After) && connected) { dprintf(stderr, "%s: %d articles sent; closing link for remote stats.\n", Pname, Stats.since_close); fflush(stderr); goodbye(WAIT); connected = 0; Stats.since_close = 0L; } if( In_Log_File ) { do { do { strcpy(old_logline, logline); (void)my_fgets(logline, MAXLOGLENGTH, fp); for (p = logline; *p && *p != '<'; p++); } while( !*p || (*(p-2) == '-') || (strnindex(p, sysname, 0) == -1)); for (i = 0; *p && *p != '>'; mesgid[i++] = *p, p++); mesgid[i++] = *p; /* go ahead and add the ">" */ mesgid[i] = '\0'; for (p += 2, i = 0; *p && (*p !=' '); shortfn[i++] = *p, p++); shortfn[i] = '\0'; strcpy(filename, SPOOL_DIR); strcat(filename, shortfn); dprintf(stderr, "fn=%s, id=%s\n", filename, mesgid); if ((newfp = fopen(filename, mode)) == (FILE *)NULL) { /* ** The only permissible error is `file non-existant' ** anything else indicates something is seriously ** wrong, and we should go away to let the shell ** script clean up. */ if (errno != ENOENT) { char buf[BUFSIZ]; sprintf(buf, E_fopen, filename, mode, errmsg(errno)); /* log(L_WARNING, buf); */ goodbye(DONT_WAIT); exit(EX_OSERR); } } } while (newfp == (FILE *)NULL); } else while(newfp == (FILE *)NULL) { if (fgets(filename, fnlen, fp) == (char *)NULL) { dprintf(stderr,"%s: getfp: at EOF\n", Pname); /* return((FILE *)NULL); /* EOF, tell caller */ break; } filename[fnlen - 1] = '\0'; /* make sure */ /* if fgets() ever forgets the '\n', we're fucked */ if (*(cp = &filename[strlen(filename) - 1]) == '\n') *cp = '\0'; if (filename[0] == '\0') continue; /** ** I don't care if it's separated by a space, or a tab, but I want ** the message-Id if it's in there. **/ if (index(filename, ' ') != NULL) { /** Ala C-news **/ sscanf(filename, "%s %s", buffer, mesgid); strcpy(filename, buffer); if (!cnews) { ++cnews; dprintf(stderr, "Hmmm... Config error, configed for B-news, but\n"); dprintf(stderr, " running C-news.\n"); } } else if (index(filename, '\t') != NULL) { sscanf(filename, "%s\t%s", buffer, mesgid); /** Ala B-news **/ strcpy(filename, buffer); if (cnews) { cnews = 0; dprintf(stderr, "Hmmm... Config error, configed for C-news, but\n"); dprintf(stderr, " running B-news.\n"); } } else strcpy(mesgid, ""); if ((newfp = fopen(filename, mode)) == (FILE *)NULL) { /* ** The only permissible error is `file non-existant' ** anything else indicates something is seriously ** wrong, and we should go away to let the shell ** script clean up. */ if (errno != ENOENT) { char buf[BUFSIZ]; sprintf(buf, E_fopen, filename, mode, errmsg(errno)); /* log(L_WARNING, buf); */ goodbye(DONT_WAIT); exit(EX_OSERR); } } } if (!connected) if (hello(host, transport) == FAIL) { sleep(Sleep_Time); return(NULL); } else connected = TRUE; return(newfp); }