OpenVMS Source Code Demos
NSR_COUNTER
//===============================================================================================================================
// title : nsr_count.c
// author : Neil Rieck
// started: 2013-11-28
// purpose: our current very cool graphical page counter (written my Muhammad A Muquit between 1995 and 1999 when web-servers
// were powerful machines serving up mostly static pages to underpowered PCs running Pentium-2 and Pentium-3 CPUs)
// seems to eat up more server resources than I am now comfortable with. This replacement counter attempts to shift the
// "compute burden" from our server to the client. This is just an experiment.
// target : Apache/2.0.63 (OpenVMS)
// notes : the compiled size of count.exe on OpenVMS is 342k while this program (nsr_count_100.c) is only 4k
// (adding bells-n-whistles may make it a little larger)
//
// ver who when what
// --- --- ------ ---------------------------------------------------------------------------------------------------------------
// 100 NSR 131128 1. started this experimental hack
// NSR 131129 2. added support for processing query_string data ("df=" etc.)
// NSR 131202 3. added support for parameter "st="
// 101 NSR 131203 1. now use the file-i/o logic found in wwwcount (for compatability + portability reasons)
//===============================================================================================================================
#include <stdio.h> // standard i/o
#include <stdlib.h> // standard library
#include <string.h> // string routines
//
#ifdef __VMS
#include <fcntl.h>
#include <unixio.h> // open() etc.
#endif
//
#define DATA_DIR "wwwcount_etc:[data]" // counter files live here on OpenVMS
#define MAX_PARAM_SIZE 30 //
//
char query_string[128]; //
char temp[128]; //
char p_df[MAX_PARAM_SIZE]; // parameter "df=" (Data File)
char p_st[MAX_PARAM_SIZE]; // parameter "st=" (STarting count)
char count_fs[120]; // counter filename specification
char file_buffer[80]; // for reading from the counter file
char msg_buffer[80]; //
long long start_value; //
long long count_value; //
long junk1; //
long junk2; //
long junk3; //
long dollar_found; //
long pipe1; //
long pipe2; //
long fd; // not a pointer with open/lock/close etc.
char *tmpPtr; //
//=================================================================
// this function works like BASIC's seg$() function like so:
// s1$ = seg$(s2$,1,9)
//=====================================================================
void strseg(char *s1, char *s2, long const p1, long const p2){
long t0, t1, t2; //
t0 = 0; //
t1 = p1; // starting char
t2 = p2; // ending char
while (t1<=t2){ //
s1[t0] = s2[t1]; // copy char-by-char
t0++; //
t1++; //
}
s1[t0] = '\0'; // terminate string 1
}
//
// the big kahoona
//
void main(){
//
// initializations
//
temp[0] = '\0'; // workbench
p_df[0] = '\0'; // parameter "df=" (Data File)
p_st[0] = '\0'; // parameter "st=" (STarting count)
file_buffer[0] = '\0'; //
msg_buffer[0] = '\0'; //
dollar_found = 0; //
start_value = 0; //
count_value = 0; //
//
// prep to send back a plain-text HTTP response
// (error messages will also be returned in this stream)
//
printf("Status: 200\n"); // this is for Apache (it will be changed)
printf("Content-type: text/plain\n"); // tell client browser that this is plain text
printf("Cache-Control: no-cache, no-store\n"); // this client browser not to cache this data
printf("\n"); // end of HTTP header
//
// read query string (do not allow hackers to use a buffer overflow exploit)
//
// note: this is data following the question mark as seen in this example
// http://kawc09.on.bell.ca/scripts/count?df=icsis_20010101.DAT|dd=d|reload=T
//
tmpPtr = getenv("query_string"); // this is set by Apache
if (tmpPtr==NULL){ //
fprintf(stderr,"-e-error, could not locate QUERY_STRING\n"); // write to Apache log
fprintf(stdout,"-e-error, could not locate QUERY_STRING\n"); // write to client
return; //
}
if (strlen(tmpPtr)>=(sizeof(query_string)-3)){ //
fprintf(stderr,"-e-error, QUERY_STRING is too large (hacking?)\n"); // write to Apache log
fprintf(stdout,"-e-error, QUERY_STRING is too large (hacking?)\n"); // write to client
return; //
}
sprintf(query_string, "%c%s%c",'|',tmpPtr,'|'); // tack pipes on each end for easier processing
//
// okay, scan query string looking for our parameters (we only care about "df=" and "st=")
//
junk1 = strlen(query_string); //
pipe1 = 0; // there is always one pipe at position #0
pipe2 = 0; // init to same as pipe1
//
get_more_params: //
for (junk2=pipe1+1; junk2<junk1; junk2++){ // scan query_string starting with pos-1
if (query_string[junk2]=='|'){ // if this is a pipe
pipe2 = junk2; // remember this position
//
junk3 = pipe2 - pipe1 - 1; // compute the resultant data length
if ((junk3 > 0) && (junk3 < MAX_PARAM_SIZE)){ // if not null, and we have room to store it
strseg(temp,query_string,pipe1+1,pipe2-1); // copy data found between the pipes
// fprintf(stderr,"-i-param %s\n",temp); // debug to the Apache error log
if (strncasecmp(temp,"DF=",3)==0){ //
strseg(p_df,temp,3,strlen(temp)); //
sprintf(count_fs,"%s%s",DATA_DIR,p_df); //
// fprintf(stderr,"-i-fs: %s\n",count_fs); // debug to the Apache error log
}
if (strncasecmp(temp,"ST=",3)==0){ //
strseg(p_st,temp,3,strlen(temp)); //
if (strlen(p_st)<=13){ // if <= characters for 9 trillion
start_value = atoll(p_st); //
if (start_value<0) //
start_value = 1; //
}else{ //
start_value = 0; //
}
// fprintf(stderr,"-i-st: %d\n",start_value); // debug to the Apache error log
} //
} //
pipe1 = pipe2; // prep for next chunk
} //
} //
no_more_params:;
//
// okay, lets read the counter file
// note: for compatability reasons, this open/read code is the same as "wwwcount2_6/src/rwdata.c"
//
#ifdef SYS_WIN32
fd = sopen(count_fs,_O_RDWR|_O_CREAT,_SH_DENYWR,_S_IREAD|_S_IWRITE);
#else
#ifdef __VMS
fd = open(count_fs,O_RDWR|O_CREAT,0644,"shr=put","ctx=rec"); // VMS, OpenVMS
#else
fd = open(count_fs,O_RDWR|O_CREAT,0644); // UNIX, Linux, etc.
#endif
#endif
if (fd<0){ //
printf("-e-oops, file open error: %d",fd); //
return; //
} //
#ifdef SYS_UNIX
SetLock(fd); //
#endif
lseek(fd,0L,0); // rewind
//
junk1 = read(fd,file_buffer,80); // read the file (unix style)
if (junk1<=0){ // oops, nothing here (must be a new file)
if (start_value>0){ //
count_value=start_value; //
}else{ //
count_value=0; //
} //
sprintf(file_buffer,"%lld",count_value); // write this data to the disk buffer
junk1 = strlen(file_buffer); // find the string count before continuing
} //
//
// wwwcount2_6 places a dollar sign after the ascii data string. Do not display it.
//
for (junk2=0; junk2<junk1; junk2++){ //
if (file_buffer[junk2]=='$'){ // if we detected the optional dollar sign
dollar_found = 1; // then remember this fact
msg_buffer[junk2] = '\0'; // and also zap it
exit; //
}else{ //
msg_buffer[junk2] = file_buffer[junk2]; // copy char-by-char
} //
} //
count_value = atoll(msg_buffer); //
count_value++;
if (dollar_found==1){
sprintf(file_buffer,"%lld%c",count_value,'$');
}else{
sprintf(file_buffer,"%lld",count_value);
}
sprintf(msg_buffer,"%lld",count_value);
junk1 = strlen(msg_buffer); //
while (junk1<6){ //
printf("0"); // need some zero padding
junk1++; //
} //
printf("%s",msg_buffer); // this goes back to the client
//
// write changes back to disk
//
lseek(fd,0L,0); // rewind
junk3 = write(fd,file_buffer,strlen(file_buffer)); //
(void) close(fd);
//
#ifdef SYS_UNIX
UnsetLock(fd);
#endif
}