Thursday, September 27, 2007

Richard Stevens My Hero

In my earlier days, I was a struggling programmer. Not married and had plenty of time to learn things. What struck me in those days was the beauty of Unix and the ugliness of Windows (things have not changed even today). What helped me the most in my quest for knowledge was the book "Unix Network Programming" by Richard Stevens. Stevens died in 1999, at the age of 48. In 2000, he was posthumously awarded the Usenix Lifetime Achievement Award. May his soul rest in peace and may god give happiness to all his loved ones. If I have a hero other than Dr Abdul Kalam, it is Richard Stevens and D.J Bernstein who has written the world's first flawless software called qmail all written in the C Programming Language. When I wrote my first socket program way back in 1995 I used Richard Steven's tcp_open(). All aspiring C Programmers and Unix enthusiasts should read each and every book by Richard Stevens. Since the time I read this function in his book, it is being used even today in all my programs involving TCP connections. Here it is

/*
* $Log: tcpopen.c,v $
* Revision 2.2 2002-12-11 10:28:57+05:30 Cprogrammer
* added ERESTART check for errno
*
* Revision 2.1 2002-12-02 21:48:45+05:30 Cprogrammer
* added check for in_addr_t
*
* Revision 1.5 2002-03-03 15:40:47+05:30 Cprogrammer
* changed strncpy to scopy
*
* Revision 1.4 2001-12-19 16:29:10+05:30 Cprogrammer
* added code to bind on unix domain sockets
*
* Revision 1.3 2001-12-13 11:52:27+05:30 Cprogrammer
* variable for inaddr changed to in_addr_t
*
* Revision 1.2 2001-12-11 11:32:53+05:30 Cprogrammer
* inclusion of systeminfo.h for solaris
*
* Revision 1.1 2001-12-07 22:29:09+05:30 Cprogrammer
* Initial revision
*
*/

#ifndef lint
static char sccsid[] = "$Id: tcpopen.c,v 2.2 2002-12-11 10:28:57+05:30 Cprogrammer Stab root $";
#endif

#include "stdlib.h"
#include "netdb.h"
#include "unistd.h"
#include "netinet/in.h"
#include "arpa/inet.h"
#include "string.h"
#include "errno.h"
#include "sys/param.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "sys/un.h"
#ifdef sun
#include "sys/systeminfo.h"
#endif


static unsigned sleeptime = MAXSLEEP + 1; /*- 0 for infinite connect */

int
tcpopen(host, service, port) /*- Thanks to Richard's Steven */
char *host;
char *service;
int port;
/*-
* host - Name or dotted decimal address of the
* other system / Pathname for a unix domain socket
* service - Name of the service being requested.
* Can be NULL, if port > 0.
* port - if == 0, nothin special, use port# of the service.
* if <> 0, it is the port# of server (host-byte-order)
*/
{
int resvport, fd, optval, retval;
char *ptr, *hostptr;
#ifdef HAVE_IN_ADDR_T
in_addr_t inaddr;
#else
unsigned long inaddr;
#endif
struct servent *sp;
struct hostent *hp;
struct sockaddr_in tcp_srv_addr;/*- server's Internet socket address */
struct sockaddr_un unixaddr; /*- server's local unix socket address */
struct servent tcp_serv_info; /*- from getservbyname */
struct hostent tcp_host_info; /*- from gethostbyname */
struct linger linger;
char *dir;
char localhost[MAXHOSTNAMELEN];

if(host && *host && ((strchr(host, '/') || ((dir = Dirname(host)) && !access(dir, F_OK)))))
{
if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
return -1;
unixaddr.sun_family = AF_UNIX;
scopy (unixaddr.sun_path, host, sizeof(unixaddr.sun_path));
if (connect (fd, (struct sockaddr *) &unixaddr, sizeof(struct sockaddr_un) ) == -1)
return -1;
return(fd);
}
/*
* Initialize the server's Internet address structure. We'll store
* the actual 4-byte Internet address and the 2-byte port # below.
*/
(void) memset((char *) &tcp_srv_addr, 0, sizeof(tcp_srv_addr));
tcp_srv_addr.sin_family = AF_INET;
if (service != (char *) NULL)
{
if ((sp = getservbyname(service, "tcp")) == NULL)
{
errno = EINVAL;
return (-1);
}
tcp_serv_info = *sp;
if (port > 0)
tcp_srv_addr.sin_port = htons(port); /*- caller's value */
else
tcp_srv_addr.sin_port = sp->s_port; /*- service's value */
} else
if (port <= 0) { errno = EINVAL; return (-1); } else tcp_srv_addr.sin_port = htons(port); #ifdef sun if (sysinfo(SI_HOSTNAME, localhost, MAXHOSTNAMELEN) > MAXHOSTNAMELEN)
#else
if (gethostname(localhost, MAXHOSTNAMELEN))
#endif
return (-1);
if (!strcmp(host, localhost) || !strcmp(host, "localhost"))
hostptr = "localhost";
else
hostptr = host;
/*
* First try to convert the hostname as the dotted decimal number.
* Only if that fails, call gethostbyname.
*/
if ((inaddr = inet_addr(hostptr)) != INADDR_NONE)
{ /*- It's a dotted decimal */
(void) memcpy((char *) &tcp_srv_addr.sin_addr, (char *) &inaddr, sizeof(inaddr));
tcp_host_info.h_name = NULL;
} else
if ((hp = gethostbyname(hostptr)) == NULL)
{
errno = EINVAL;
return (-1);
} else
{
tcp_host_info = *hp; /*- found it by name, structure copy */
(void) memcpy((char *) &tcp_srv_addr.sin_addr, hp->h_addr, hp->h_length);
}
if ((ptr = (char *) getenv("SLEEPTIME")) != (char *) 0)
{
if (isnum(ptr))
sleeptime = atoi(ptr);
else
{
errno = EINVAL;
return (-1);
}
}
for (;;)
{
if (port >= 0)
{
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < resvport =" IPPORT_RESERVED" fd =" rresvport(&resvport))" optval =" 1;" errno =" 0;;)" retval =" connect(fd," errno ="="" errno ="="" errno ="="" errno =" ECONNREFUSED;" errno =" ECONNREFUSED;" errno ="="" optval =" errno;" errno =" optval;" errno =" 0;;)" l_onoff =" 1;" l_linger =" 1;">
#include
#include
#include

/* set the socket buffer sizes */
int
setsockbuf(fd, option, size)
int fd, option, size;
{
int len, retrycount;

len = size;
for (retrycount = 0; retrycount < MAXNOBUFRETRY; retrycount++)
{
if (setsockopt(fd, SOL_SOCKET, option, (void *) &len, sizeof(int)) == -1)
{
if (errno == ENOBUFS)
{
usleep(1000);
continue;
}
close(fd);
return (-1);
}
break;
}
return (errno = 0);
}

#include "ctype.h"

int
isnum(str)
char *str;
{
char *ptr;

for (ptr = str; *ptr; ptr++)
if (!isdigit((int) *ptr))
return (0);
return (1);
}

Making Arguments

Sometimes you have a string and would want to run a system call like the execv() family calls. The exec() family of functions replaces the current process image with a new process image.
The initial argument for these functions is the pathname of a file which is to be executed. The second argument points to array of pointers to null-terminated strings that represent the argument list available to the new program.

The function below helps you to prepare the second argument for execv() or execvp() from a string. If you use the function and you are using fork(), you should in the parent, free the memory allocated by using FreeMakeArgs().

/*
* $Log: MakeArgs.c,v $
* Revision 2.4 2005-03-30 22:52:47+05:30 Cprogrammer
* BUG - Incorrect free
*
* Revision 2.3 2004-07-12 22:47:58+05:30 Cprogrammer
* bug fix. Free all allocated members
*
* Revision 2.2 2002-12-21 18:21:09+05:30 Cprogrammer
* added functionality of escaping text via quotes
*
* Revision 2.1 2002-08-13 20:35:44+05:30 Cprogrammer
* addition spaces were not getting skipped
*
* Revision 1.2 2002-03-03 17:23:05+05:30 Cprogrammer
* replaced strcpy with scopy
*
* Revision 1.1 2001-12-13 01:46:09+05:30 Cprogrammer
* Initial revision
*
*/
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "ctype.h"

#ifndef lint
static char sccsid[] = "$Id: MakeArgs.c,v 2.4 2005-03-30 22:52:47+05:30 Cprogrammer Stab root $";
#endif

#define isEscape(ch) ((ch) == '"' || (ch) == '\'')

/*
* function to expand a string into command line
* arguments. To free memory allocated by this
* function the following should be done
*
* free(argv); free(argv[0]);
*
*/
char **
MakeArgs(char *cmmd)
{
char *ptr, *sptr, *marker;
char **argv;
int argc, idx;

for(ptr = cmmd;*ptr && isspace((int) *ptr);ptr++);
idx = strlen(ptr);
if(!(sptr = (char *) malloc((idx + 1) * sizeof(char))))
return((char **) 0);
strncpy(sptr, ptr, idx + 1);
/*-
* Get the number of arguments by counting
* white spaces. Allow escape via the double
* quotes character at the first word
*/
for (argc = 0, ptr = sptr;*ptr;)
{
for(;*ptr && isspace((int) *ptr);ptr++);
if(!*ptr)
break;
argc++;
marker = ptr;
for(;*ptr && !isspace((int) *ptr);ptr++)
{
if(ptr == marker && isEscape(*ptr))
{
for (ptr++;*ptr && !isEscape(*ptr);ptr++);
if(!*ptr)
ptr = marker;
}
}
}
#ifdef DEBUG
printf("argc = %d\n", argc);
#endif
/*
* Allocate memory to store the arguments
* Do not bother extra bytes occupied by
* white space characters.
*/
if (! (argv = (char **) malloc((argc + 1) * sizeof(char *))))
return ((char **) NULL);
for (idx = 0, ptr = sptr;*ptr;)
{
for(;*ptr && isspace((int) *ptr);ptr++)
*ptr = 0;
if(!*ptr)
break;
argv[idx++] = ptr;
marker = ptr;
for(;*ptr && !isspace((int) *ptr);ptr++)
{
if(ptr == marker && isEscape(*ptr))
{
for (ptr++;*ptr && !isEscape(*ptr);ptr++);
if(!*ptr)
ptr = marker;
else /*- Remove the quotes */
{
argv[idx - 1] += 1;
*ptr = 0;
}
}
}
}
argv[idx++] = (char *) 0;
#ifdef DEBUG
for(idx = 0;idx <= argc;idx++)
printf("argv[%d] = [%s]\n", idx, argv[idx]);
#endif
return (argv);
}

void
FreeMakeArgs(char **argv)
{
free(argv[0]);
free(argv);
return;
}