Real World Example
#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);
}