Real World Example


Multi-Threaded Finger


#include <pthreadutil.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <stdio.h>
#include <ctype.h>

/*
 * These globals are set initialy and then are only read.
 * They do not need mutexes.
 */
extern int lflag;
char myhostname[MAXHOSTNAMELEN];

/* 
 * These globals change and therefore do need mutexes
 */
pthread_mutex_t spmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t spcond = PTHREAD_COND_INITIALIZER;
struct servent *sp = NULL;

void netsetup(void)
{
    pthread_mutex_lock(&spmutex); 
    if (sp) {
	fprintf(stderr, "finger: service pointer already initialized.\n");
	exit(2);
    }
    if ((sp = (struct servent *)malloc(sizeof(struct servent) + 4096)) == NULL){
	fprintf(stderr, "finger: Couldn't allocate service pointer.\n");
	exit(2);
    }
    if (getservbyname_r("finger", "tcp", sp, (char *)sp + sizeof(struct servent), 4096) == NULL) {
	fprintf(stderr, "finger: tcp/finger: unknown service\n");
	exit(2);
    }
    if (gethostname(myhostname, MAXHOSTNAMELEN)) {
	fprintf(stderr, "finger: couldn't get my hostname.\n");
	exit(2);
    }
    pthread_cond_broadcast(&spcond);
    pthread_mutex_unlock(&spmutex); 
}

void netsetupwait(void)
{
    pthread_mutex_lock(&spmutex);
    while(sp == NULL) {
	pthread_cond_wait(&spcond, &spmutex);
    }
    pthread_mutex_unlock(&spmutex);
}

void *netfinger(char *name)
{
register int c, lastc;
struct in_addr defaddr;
struct hostent *hp;
struct sockaddr_in sin;
int s, i, readbuflen;
char readbuf[1024];
char *host;

netsetupwait();
pthread_cleanup_push(fflush, NULL);

    if (!(host = strrchr(name, '@'))) {
	host = myhostname;
    } else {
	*host++ = '\0';
    }
    if (!(hp = gethostbyname(host))) {
	if ((defaddr.s_addr = inet_addr(host)) < 0) {
	    fprintf(stderr, "[%s] gethostbyname: Unknown host\n", host);
	    return;
	}
    }
    sin.sin_family = hp->h_addrtype;
    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
    sin.sin_port = sp->s_port;

    if ((s = socket(sin.sin_family, SOCK_STREAM, 0)) < 0) {
	sprintf(readbuf, "[%s]: socket", hp->h_name);
	perror(readbuf);
	return;
    }

    /* have network connection; identify the host connected with */
    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
	sprintf(readbuf, "[%s]: connect", hp->h_name);
	perror(readbuf);
	close(s);
	return;
    }

    /* -l flag for remote fingerd  */
    if (lflag)
	write(s, "/W ", 3);
    /* send the name followed by  */
    write(s, name, strlen(name));
    write(s, "\r\n", 2);

/*
 * Read from the remote system; once we're connected, we assume some
 * data.  If none arrives, we hang until the user interrupts, or
 * until the thread timeout expires.
 *
 * If we see a  or a  with the high bit set, treat it as
 * a newline; if followed by a newline character, only output one
 * newline.
 *
 * Otherwise, all high bits are stripped; if it isn't printable and
 * it isn't a space, we can simply set the 7th bit.  Every ASCII
 * character with bit 7 set is printable.
 */ 
    for (readbuflen = read(s, readbuf, 1024), flockfile(stdout), lastc = '\n',
      printf("[%s]\n", hp->h_name); readbuflen > 0; 
      readbuflen = read(s, readbuf, 1024)) {
	for (i = 0; i < readbuflen; i++) {
	    c = readbuf[i] & 0x7f;
	    if (c == 0x0d) {
		c = '\n';
		lastc = '\r';
	    } else {
		if (!isprint(c) && !isspace(c))
	  	    c |= 0x40;
		if (lastc != '\r' || c != '\n')
		    lastc = c;
		else {
		    lastc = '\n';
		    continue;
		}
	    }
  	    putchar_unlocked(c);
	}
    }
    if (lastc != '\n')
	putchar_unlocked('\n');
    pthread_cleanup_pop(1);
    funlockfile(stdout);
}


[TOP] [BACK] [FORWARD]


Prepared by Chris Provenzano (proven@mit.edu)