Skip to content

Instantly share code, notes, and snippets.

@tuklusan
Last active April 19, 2021 18:06
Show Gist options
  • Save tuklusan/bc4825acd6e4980ef2764e1b7bb3c4ff to your computer and use it in GitHub Desktop.
Save tuklusan/bc4825acd6e4980ef2764e1b7bb3c4ff to your computer and use it in GitHub Desktop.
syslogd for remote logging OpenVMS Alpha and OpenVMS VAX system logger to unix/linux rsyslog syslog server daemon. Tweaked from Doug O'Neal's syslog.c. More: http://supratim-sanyal.blogspot.com/2016/11/openvms-log-files-remote-logging-to.html
/*
* syslogd.c
* Take OPCOM messages received over a set of pseduo-ttys and redirect
* them to a UNIX syslogd. Tested with Multinet and UCX software under
* VMS 1.5 & 6.1 (AXP) and VMS 5.5 & 6.0 (VAX).
*
* The chan structure defines the translation of the opcom classes to
* Unix facilities/priorities. The Unix server is defined via the
* logical name SYSLOGD_SERVER in the LNM$SYSTEM_TABLE logical name table.
* Either a host name or an IP address may be used.
*
* must be linked against either
* multinet:multinet_socket_library/share
* or
* sys$share:ucx$ipc_shr/share
*
* On an AXP system with UCX 3.1, /STANDARD=VAXC must be specified for CC.
*
* Doug O'Neal
* Homewood Academic Computing
* Johns Hopkins University
* [email protected]
*
* Modified by Supratim Sanyal (supratim at riseup.net):
* - 14-NOV-2016: Added logical name SYSLOGD_PORT to specify the port number of
* remote syslog server - required in addition to SYSLOGD_SERVER
* in the LNM$SYSTEM_TABLE logical name table.
* - 01-NOV-2017: Worked around ftname[retlen-1] which was crashing in EXE by
* HP C V7.3-009 on OpenVMS Alpha V8.3
* http://supratim-sanyal.blogspot.com/2016/11/openvms-log-files-remote-logging-to.html
*/
#define UCX 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <ssdef.h>
#include <dvidef.h>
#include <descrip.h>
#include <stsdef.h>
#include <lnmdef.h>
#include <iodef.h>
#include <opcdef.h>
#ifdef MULTINET
#include "multinet_root:[multinet.include.sys]types.h"
#include "multinet_root:[multinet.include.sys]socket.h"
#include "multinet_root:[multinet.include.netinet]in.h"
#include "multinet_root:[multinet.include]netdb.h"
#endif
#ifdef UCX
#include <types.h>
#include <socket.h>
#include <in.h>
#include <netdb.h>
#include <ucx$inetdef.h>
#endif
unsigned int PTD$CREATE();
unsigned int PTD$READ();
unsigned int PTD$READW();
unsigned int SYS$QIOW();
unsigned int SYS$EXPREG();
unsigned int SYS$GETDVIW();
unsigned int SYS$SNDOPR();
unsigned int SYS$TRNLNM();
unsigned int write_syslog();
int sockfd;
struct sockaddr_in cli_addr, serv_addr;
#define FT_DATA_MAX (unsigned short) 508
struct ft_buffer {
unsigned short status, count;
unsigned char data[FT_DATA_MAX];
};
struct iosb {
unsigned short condition, transfer_count;
unsigned long info;
};
// #define SERV_UDP_PORT 514
// #define SERV_UDP_PORT 65514
int SERV_UDP_PORT=514;
/*
* Facility codes
*/
#define LOG_KERN (0<<3) /* kernel messages */
#define LOG_USER (1<<3) /* random user-level messages */
#define LOG_MAIL (2<<3) /* mail system */
#define LOG_DAEMON (3<<3) /* system daemons */
#define LOG_AUTH (4<<3) /* security/authorization messages */
#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
#define LOG_LPR (6<<3) /* line printer subsystem */
#define LOG_NEWS (7<<3) /* netnews subsystem */
#define LOG_UUCP (8<<3) /* uucp subsystem */
#define LOG_CRON (15<<3) /* cron/at subsystem */
#define LOG_LOCAL0 (16<<3) /* reserved for local use */
#define LOG_LOCAL1 (17<<3) /* reserved for local use */
#define LOG_LOCAL2 (18<<3) /* reserved for local use */
#define LOG_LOCAL3 (19<<3) /* reserved for local use */
#define LOG_LOCAL4 (20<<3) /* reserved for local use */
#define LOG_LOCAL5 (21<<3) /* reserved for local use */
#define LOG_LOCAL6 (22<<3) /* reserved for local use */
#define LOG_LOCAL7 (23<<3) /* reserved for local use */
/*
* Priorities (these are ordered)
*/
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but signification condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
struct {
unsigned short chan;
unsigned int class, priority;
struct ft_buffer *buffer;
} chan[] =
{ {0, OPC$M_NM_CENTRL|OPC$M_NM_REPLY, LOG_USER|LOG_NOTICE, 0},
{0, OPC$M_NM_PRINT|OPC$M_NM_CARDS, LOG_LPR|LOG_INFO, 0},
{0, OPC$M_NM_TAPES|OPC$M_NM_DISKS|OPC$M_NM_DEVICE, LOG_KERN|LOG_ERR, 0},
{0, OPC$M_NM_NTWORK, LOG_DAEMON|LOG_INFO, 0},
{0, OPC$M_NM_CLUSTER, LOG_KERN|LOG_CRIT, 0},
{0, OPC$M_NM_SECURITY, LOG_AUTH|LOG_WARNING, 0},
{0, OPC$M_NM_SOFTWARE|OPC$M_NM_LICENSE, LOG_KERN|LOG_WARNING, 0},
{0, OPC$M_NM_OPER1|OPC$M_NM_OPER2|OPC$M_NM_OPER3|OPC$M_NM_OPER4|
OPC$M_NM_OPER5|OPC$M_NM_OPER6|OPC$M_NM_OPER7|OPC$M_NM_OPER8|
OPC$M_NM_OPER9|OPC$M_NM_OPER10|OPC$M_NM_OPER11|OPC$M_NM_OPER12,
LOG_LOCAL0|LOG_INFO, 0}
};
#ifdef UCX
void bzero(c,length)
char *c; int length;
{
char *c1;
for (c1=c; length--; ) *c1++=0;
}
void bcopy(c1, c2, length)
char *c1, *c2; int length;
{
if (length==0 || c1==c2) return;
if (c1 > c2) {
for (;length--;) *c2++ = *c1++;
} else {
c1 += length;
c2 += length;
for (;length--;) *--c2 = *--c1;
}
}
#endif
main(argc, argv)
int argc; char *argv[];
{
unsigned int status, acmode, charbuff, buflen, astadr, astprm;
unsigned int ast_acmode, inadr[2], retlen;
unsigned short i;
unsigned int nchan, ichan;
struct {
unsigned short length, code;
unsigned long bufaddr, retlenaddr;
unsigned long zero;
} itemlist;
struct {
unsigned char class, type;
unsigned short width;
unsigned char basic[3], length;
unsigned int extended;
} term_char;
struct {
unsigned char type, enab[3];
unsigned int mask;
unsigned short ounit;
unsigned char str_length, string[16];
} msgbuf;
char ftname[64];
struct dsc$descriptor_s enable;
struct iosb iosb;
$DESCRIPTOR(tabnam,"LNM$SYSTEM_TABLE");
$DESCRIPTOR(syslog_host,"SYSLOGD_SERVER");
$DESCRIPTOR(syslog_port,"SYSLOGD_PORT");
char serv_host_name[255];
char serv_port[255];
struct hostent *serv_host_addr;
nchan = sizeof chan / sizeof chan[0];
memset(ftname,0,sizeof(ftname));
/*
* Set up the UDP connection to the syslog server
*/
/*
* Fill in the structure "serv_addr" with the address of the
* server that we want to send to.
*/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
/*
* first, check that we can get the IP address of the syslogd server
*/
itemlist.length = sizeof(serv_host_name);
itemlist.code = LNM$_STRING;
itemlist.bufaddr = serv_host_name;
itemlist.retlenaddr = &retlen;
itemlist.zero = 0;
status = SYS$TRNLNM(0,&tabnam,&syslog_host,0,&itemlist);
if (!($VMS_STATUS_SUCCESS(status))) {
fprintf(stderr,"Cannot translate logical name %s in table %s\n",syslog_host.dsc$a_pointer,tabnam.dsc$a_pointer);
exit (status);
}
serv_host_addr=gethostbyname(serv_host_name);
if (!serv_host_addr) {
serv_addr.sin_addr.s_addr = inet_addr(serv_host_name);
if (serv_addr.sin_addr.s_addr == -1) {
fprintf(stderr,"Cannot resolve name %s\n",serv_host_name);
exit (1);
}
} else {
bcopy(serv_host_addr->h_addr, (char *) &serv_addr.sin_addr,
serv_host_addr->h_length);
}
/*
* then, check that we can get the port number of the syslogd server
*/
itemlist.length = sizeof(serv_port);
itemlist.code = LNM$_STRING;
itemlist.bufaddr = serv_port;
itemlist.retlenaddr = &retlen;
itemlist.zero = 0;
status = SYS$TRNLNM(0,&tabnam,&syslog_port,0,&itemlist);
if (!($VMS_STATUS_SUCCESS(status))) {
fprintf(stderr,"Cannot translate logical name %s in table %s\n",syslog_port.dsc$a_pointer,tabnam.dsc$a_pointer);
exit (status);
}
SERV_UDP_PORT=atoi(serv_port);
if(0==SERV_UDP_PORT) {
fprintf(stderr,"Cannot translate port number from logical %s\n",syslog_host.dsc$a_pointer);
exit (1);
}
serv_addr.sin_port = htons(SERV_UDP_PORT);
/*
* Open a UDP socket (an Internet datagram socket).
*/
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("client: can't open datagram socket\n");
exit (1);
}
/*
* Bind any local address for us.
*/
bzero((char *) &cli_addr, sizeof(cli_addr)); /* zero out */
cli_addr.sin_family = AF_INET;
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
cli_addr.sin_port = htons(0);
if (bind(sockfd, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
perror("client: can't bind local address\n");
exit (1) ;
}
/*
* set up a pseudoterminal device for each operator class defined
* and enable that device through opcom
*/
for (ichan=0; ichan<nchan; ichan++) {
/* set aside memory for the pty device */
acmode = 0;
charbuff = 0;
buflen = 0;
astadr = 0;
astprm = 0;
ast_acmode = 0;
status = SYS$EXPREG(1,inadr,0,0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* create the pty device */
chan[ichan].buffer = inadr[0];
status = PTD$CREATE(&chan[ichan].chan, acmode, charbuff, buflen, astadr,
astprm, ast_acmode, inadr);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* set the pty device to maximum page width, infinite page length */
status = SYS$QIOW(0, chan[ichan].chan, IO$_SENSECHAR, &iosb, 0, 0,
&term_char, sizeof(term_char), 0, 0, 0, 0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
term_char.width=511;
term_char.length=0;
status = SYS$QIOW(0, chan[ichan].chan, IO$_SETCHAR, &iosb, 0, 0,
&term_char, sizeof(term_char), 0, 0, 0, 0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* get the pty device name */
itemlist.length = sizeof(ftname);
itemlist.code = DVI$_FULLDEVNAM;
itemlist.bufaddr = ftname;
itemlist.retlenaddr = &retlen;
itemlist.zero = 0;
status = SYS$GETDVIW(0,chan[ichan].chan,0,&itemlist,0,0,0,0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
/* ftname[retlen-1]='\0'; -- This crashes on OpenVMS 8.3 Alpha, memsetting this at declaration above - Supratim Sanyal 01-NOV-201
7 */
msgbuf.type = OPC$_RQ_TERME;
msgbuf.enab[0] = 1;
msgbuf.mask = chan[ichan].class;
for (i=retlen-2; i>0; i--) {
if ((ftname[i] >= 'A') && (ftname[i] <= 'Z')) {
msgbuf.ounit = (short) atoi(&ftname[i+1]);
ftname[i+1] = '\0';
break;
}
}
msgbuf.str_length = strlen(ftname);
strncpy(msgbuf.string,ftname,msgbuf.str_length);
enable.dsc$w_length = sizeof(msgbuf);
enable.dsc$b_dtype = DSC$K_DTYPE_T;
enable.dsc$b_class = DSC$K_CLASS_S;
enable.dsc$a_pointer = &msgbuf;
status = SYS$SNDOPR(&enable,0);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
}
/*
* Now set up async reads to each channel. As various opcom messages come
* in, the appropriate ast will fire and the message logged. Another read
* will then be queued.
*/
for (ichan=0; ichan<nchan; ichan++) {
status = PTD$READ(0, chan[ichan].chan, &write_syslog, ichan,
chan[ichan].buffer, FT_DATA_MAX);
if (!($VMS_STATUS_SUCCESS(status))) exit (status);
}
status = SYS$HIBER();
exit(status);
}
unsigned int write_syslog(ichan)
unsigned int ichan;
{
unsigned int i;
char header[1024], buffer[512], *c1;
time_t now;
(void) SYS$SETAST(0); /* block new ast's */
time(&now);
c1 = buffer;
for (i=0; i<=chan[ichan].buffer->count; i++) {
if (chan[ichan].buffer->data[i]=='\015') { /* CR delimited */
*c1 = '\0';
sprintf(header,"<%d>%.15s [OPCOM] ", chan[ichan].priority,
(ctime(&now)+4));
strcat(header,buffer);
sendto(sockfd, header, strlen(header), 0, &serv_addr,
sizeof serv_addr);
c1 = buffer;
} else if (isprint(chan[ichan].buffer->data[i])) {
*c1 = chan[ichan].buffer->data[i];
c1++;
}
}
/* queue a new read */
(void) PTD$READ(0, chan[ichan].chan, &write_syslog, ichan,
chan[ichan].buffer, FT_DATA_MAX);
(void) SYS$SETAST(1); /* re-enable ast's */
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment