/* msgidd -- message ID daemon
* vix 24may90 [written]
*
* with mods ken@sdd.hp.com 01jul90
* speedups by Geoff Collyer, 26 July 1992
*
* $Id: msgidd.c,v 1.7 1994/12/09 02:52:18 sob Exp sob $
*/
#include "common.h"
#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#define NEEDMSGS
#include "msgid.h"
#define ASSERT(e, m) if (!(e)) {fputs("assert failed... ", stderr);\
perror(m); exit(1);}
#define STREQ(s1, s2) (*(s1) == *(s2) && strcmp(s1, s2) == 0)
#define STRN_EQ(s1, s2, n) (*(s1) == *(s2) && strncmp(s1, s2, n) == 0)
#define FLAGS_RESETLOG 0x02
#define FLAGS_FLUSHLOG 0x04
#define MAX_AGE 10
#define ALARM_TIME 300
#define HASHSIZE 1024
#if 0
#define dprintf fprintf
#else
#define dprintf (void)
#endif
#ifndef __FreeBSD__
char *malloc();
extern int errno;
#endif
int log = 0, flags = 0;
time_t hold_time = MAX_AGE * 60;
char *hosts[100], *lfn, *ptime();
FILE *logfp = NULL;
struct {
int connected;
int connects, drops;
int new, dup, cancel;
int freed;
} stats;
struct el {
struct el *next;
time_t age;
int refcnt;
char id[1];
} *ids[HASHSIZE];
char *months[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static int transaction();
static unsigned
mkhash(s)
register unsigned char *s;
{
register unsigned hash = 0;
register unsigned char c;
while ((c = *s++) != '\0')
hash += c;
return hash;
}
#ifndef BSD_44
static char *
strdup(x)
char *x;
{
register char *y = malloc(strlen(x) + 1);
if (y)
strcpy(y, x);
return y;
}
#endif
static void
savepid ()
{
FILE *pidfp ;
if ((pidfp = fopen (PIDFILE,"w")) == NULL)
return ;
(void) fprintf (pidfp,"%d\n",getpid()) ;
(void) fclose (pidfp) ;
}
char *
ptime(now)
time_t now;
{
static char buf[50];
struct tm *tmp;
tmp = localtime(&now);
(void) sprintf(buf, "%s %2d %02d:%02d:%02d",
months[tmp->tm_mon], tmp->tm_mday, tmp->tm_hour,
tmp->tm_min, tmp->tm_sec);
return (buf);
}
static void
usage(me)
char *me;
{
(void) fprintf(stderr, "Usage: %s [options]\n", me);
(void) fprintf(stderr, "Options: -s <unix domain socketname> [%s]\n",
SOCKNAME);
(void) fprintf(stderr, " -l <log file name>\n");
(void) fprintf(stderr, " -h <hold time in minutes>\n");
exit(1);
}
static void
openlogfile()
{
if (logfp)
(void) fclose(logfp);
if (log && (logfp = fopen(lfn, "a+")) == NULL) {
syslog(LOG_ERR, "Unable to open %s: %m", lfn);
log = 0;
logfp = NULL;
}
}
static SIGRET
bye()
{
if (log)
(void) fclose(logfp);
dprintf(stderr,"Bye !!\n");
exit(0);
}
static SIGRET
resetlog() {
flags |= FLAGS_RESETLOG;
signal(SIGHUP, resetlog);
}
static SIGRET
pstats()
{
char msgbuf[1024];
if (log)
flags |= FLAGS_FLUSHLOG;
sprintf(msgbuf, "stats: %d connected, %d connects, %d drops, %d dups, %d new, %d cancel, %d freed\n",
stats.connected, stats.connects, stats.drops, stats.dup, stats.new,
stats.cancel, stats.freed);
dprintf(stderr, "%s\n", msgbuf);
syslog(LOG_INFO, msgbuf);
stats.connects = stats.drops = stats.new = stats.cancel = stats.dup =
stats.freed = 0;
signal(SIGALRM, pstats);
alarm(ALARM_TIME);
}
static SIGRET onpipe();
main(argc, argv)
int argc;
char *argv[];
{
register char *sn = SOCKNAME;
register int s;
int highest_fd;
struct sockaddr_un n, in;
fd_set clients;
extern char *optarg;
extern int optind;
while ((s = getopt(argc, argv, "l:h:s:")) != EOF)
switch(s) {
case 'h':
hold_time = 60 * atoi(optarg);
if (hold_time <= 0 || hold_time > (24 * 3600))
usage(argv[0]);
break;
case 's':
sn = strdup(optarg);
break;
case 'l':
log++;
lfn = strdup(optarg);
break;
default:
usage(argv[0]);
break;
}
if (optind != argc)
usage(argv[0]);
if (log) {
openlogfile();
if (!log) {
(void) fprintf(stderr, "%s: Unable to open log file (%s)\n",
argv[0], lfn);
exit(1);
}
}
savepid () ;
#ifdef SYSLOG
# ifdef LOG_DAEMON
openlog("msgidd", LOG_PID, SYSLOG);
# else
openlog("msgidd", LOG_PID);
# endif
#endif
s = socket(PF_UNIX, SOCK_STREAM, 0);
ASSERT(s>=0, "socket");
highest_fd = s;
n.sun_family = AF_UNIX;
(void) strcpy(n.sun_path, sn);
(void) unlink(sn);
ASSERT(0<=bind(s, (struct sockaddr*)&n,
strlen(n.sun_path) +sizeof n.sun_family),n.sun_path);
FD_ZERO(&clients);
listen(s, 5);
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
signal(SIGHUP, resetlog);
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, bye);
signal(SIGUSR1, bye); /* for profiling, etc. */
signal(SIGALRM, pstats);
signal(SIGPIPE, onpipe);
alarm(ALARM_TIME);
for (;;) {
register int nfound, fd;
fd_set readfds;
if (flags) {
if (flags & FLAGS_FLUSHLOG)
(void) fflush(logfp);
if (flags & FLAGS_RESETLOG)
openlogfile();
flags = 0;
}
readfds = clients; /* we want to select the clients... */
FD_SET(s, &readfds); /* ...and the main server socket. */
nfound = select(highest_fd+1, &readfds, NULL, NULL, NULL);
if (nfound < 0 && errno == EINTR)
continue;
ASSERT(0<=nfound, "select");
for (fd = 0; fd <= highest_fd; fd++) {
if (FD_ISSET(fd, &readfds)) {
if (fd == s) {
int fromlen = sizeof(in);
if ((fd = accept(s, (struct sockaddr *) &in,
&fromlen)) == -1) {
syslog(LOG_ERR, "Accept failed: %m");
} else {
FD_SET(fd, &clients);
if (fd > highest_fd)
highest_fd = fd;
stats.connects++;
stats.connected++;
}
} else if (FD_ISSET(fd, &clients)) {
if (-1 == transaction(fd)) {
close(fd);
FD_CLR(fd, &clients);
stats.connected--;
stats.drops++;
if (hosts[fd]) {
if (log) {
(void) fprintf(logfp, "%s Disconnect %s\n",
ptime(time((time_t *)0)), hosts[fd]);
}
dprintf(stderr, "Disconnect(%d/%s)\n",
fd, hosts[fd]);
free(hosts[fd]);
hosts[fd] = NULL;
}
}
} else {
dprintf(stderr, "Bad fd %d from select\n", fd);
}
}
}
}
}
int sigpiped;
static int
reply(fd, ans)
register int fd, ans;
{
int status;
sigpiped = 0;
while ((status = write(fd, (ans ? "\001" : "\000"), 1)) < 0
&& errno == EINTR && !sigpiped)
;
if (status < 0) {
if (log) {
register time_t now = time((long *)0);
fprintf(logfp, "%s write failed (fd %d)\n", ptime(now), fd);
}
syslog(LOG_ERR, "write failed, closing connection: %m");
return -1;
}
return (0);
}
static void
cancel(fd, bufp, now)
register int fd;
register char *bufp;
time_t now;
{
register struct el *i, *p;
register int found = 0, hash = mkhash(bufp) % HASHSIZE;
for (i = ids[hash], p = NULL; i; p = i, i = i->next)
if (STREQ(i->id, bufp)) {
if (p == NULL)
ids[hash] = i->next;
else
p->next = i->next;
if (log)
(void) fprintf(logfp, "%s Cancel %s %s\n",
ptime(now), hosts[fd] ? hosts[fd] : "", i->id);
free(i);
found++;
stats.cancel++;
dprintf(stderr, "Cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp);
break;
}
if (!found) {
dprintf(stderr, "Bad-cancel(%d/%s): `%s'\n", fd, hosts[fd], bufp);
syslog(LOG_ERR, "Cancel, %s not found", bufp);
if (log)
(void) fprintf(logfp, "%s Error cancel %s %s\n",
ptime(now), hosts[fd] ? hosts[fd] : "", i->id);
}
}
static void
add(fd, bufp, n, now)
register int fd;
register char *bufp;
register int n;
time_t now;
{
register struct el *i;
register int hash = mkhash(bufp) % HASHSIZE;
/* this malloc includes the id[1] array, which means
* that there's already room for strcpy's null
*/
i = (struct el *) malloc(sizeof(struct el) + n);
if (i == NULL)
return;
i->age = now;
(void) strcpy(i->id, bufp);
if (log) {
i->refcnt = 1;
(void) fprintf(logfp, "%s Add %s %s\n", ptime(now),
hosts[fd] ? hosts[fd] : "", bufp);
}
i->next = ids[hash];
ids[hash] = i;
stats.new++;
dprintf(stderr, "Add(%d/%s): `%s'\n", fd, hosts[fd], bufp);
}
static int
search(fd, bufp, now)
register int fd;
char *bufp; /* no leading '<' */
time_t now;
{
register struct el *i, *p;
register char bufc = *bufp;
register int hash = mkhash(bufp) % HASHSIZE;
int found = 0, searched = 0;
/*
* search the appropriate list.
*/
for (i = ids[hash], p = NULL; i; p = i, i = i->next) {
/*
* if too old, everything from here to the end
* can be nuked (we always add at the top).
*/
if ((now - i->age) > hold_time) {
while (i) {
register struct el *n = i->next;
if (p)
p->next = n;
else
ids[hash] = n;
free((char *)i);
i = n;
stats.freed++;
}
break;
}
searched++;
if (STREQ(i->id, bufp)) {
if (log) {
i->refcnt++;
(void) fprintf(logfp, "%s Lose %s %d %ld %s\n",
ptime(now), hosts[fd] ? hosts[fd] : "", i->refcnt,
(now - i->age), i->id);
}
found++;
stats.dup++;
break;
}
}
dprintf(stderr, "Search(%d/%s): %s(%d) `%s'\n",
fd, hosts[fd], (found ? "dup" : "new"), searched, bufp);
return (found);
}
/* returns: -1 == client is gone, close this fd please
* 0 == success
*/
static int
transaction(fd)
register fd;
{
char buf[1023];
register int n;
register char *bufp, *cmdp;
register time_t now = time((long *)0);
/* read the request. zero-length read means connection is gone.
*/
do {
n = read(fd, buf, sizeof(buf));
if (n == 0)
return -1;
} while (n < 0 && errno == EINTR);
ASSERT(n>0, "read");
if (hosts[fd]) {
dprintf(stderr, "Parse(%d/%s): `%s'\n", fd, hosts[fd], buf);
}
/* Separate cmd from id
*/
cmdp = buf;
bufp = buf + 4;
n -= 4;
/* find the first useful character, saving it and its address.
*/
if (*bufp == '<') {
bufp++;
n--;
}
/* rip out useless characters at end, remembering real length.
*/
while (n > 0) {
register x = n - 1;
register ch = bufp[x];
if (ch == '\n' || ch == '\r' || ch == '>')
n = x;
else
break;
}
bufp[n] = '\0';
/*
* Which cmd ?
*/
if (STRN_EQ(cmdp, msgs[MCANCEL], 4)) {
cancel(fd, bufp, now);
return reply(fd, 0);
} else if (STRN_EQ(cmdp, msgs[MADD], 4)) {
if (search(fd, bufp, now))
return reply(fd, 1);
else {
add(fd, bufp, n, now);
return reply(fd, 0);
}
} else if (STRN_EQ(cmdp, msgs[MOLD], 4)) {
if (log)
(void) fprintf(logfp, "%s Old %s %s\n", ptime(now),
hosts[fd] ? hosts[fd] : "", bufp);
dprintf(stderr, "Old(%d/%s): `%s'\n", fd, hosts[fd], bufp);
return reply(fd, 0);
} else if (STRN_EQ(cmdp, msgs[MHOST], 4)) {
if (hosts[fd])
free(hosts[fd]);
hosts[fd] = strdup(bufp);
if (log)
(void) fprintf(logfp, "%s Connect %s\n", ptime(now), hosts[fd]);
dprintf(stderr, "Connect(%d/%s)\n", fd, hosts[fd]);
return reply(fd, 0);
} else {
syslog(LOG_ERR, "Unknown command %s", cmdp);
if (log)
(void) fprintf(logfp, "%s Error %s unknown-cmd %s\n", ptime(now),
hosts[fd], cmdp);
dprintf(stderr, "Error(%d/%s) unknown-cmd %s\n", fd, hosts[fd], cmdp);
return reply(fd, 0);
}
#ifdef lint
/*NOTREACHED*/
return (0);
#endif
}
static SIGRET onpipe()
{
register time_t now = time((long *)0);
if (log)
fprintf(logfp, "%s Got SIGPIPE\n", ptime(now));
sigpiped++;
signal(SIGPIPE, onpipe);
}
syntax highlighted by Code2HTML, v. 0.9.1