2.11BSD/src/new/notes/src/misc.c
#include <errno.h>
#include "parms.h"
#include "structs.h"
#include <signal.h> /* signal processing */
#include <ctype.h>
#ifdef SIGCHLD
#include <sys/wait.h> /* for child status */
#endif SIGCHLD
#ifdef RCSIDENT
static char rcsid[] = "$Header: src/usenet/notes/src/RCS/misc.c,v 1.9.1 96/3/21 21:49:42 paul Exp $";
#endif RCSIDENT
#define LOCKTRY 10 /* number of shots at grabbing */
#define FORKTRY 10 /* tries at forking */
/*
* dounix(charstring, flag) will execute that character string as a shell command
* stolen from shell, though this one catches more signals.
*
* Depending on the RUNSUID flag the routine sets things back to
* the users group or uid. Early versions were setuid and newer
* versions only run setgid. Don't get confused by the "hisuid"
* argument: it really means "reset to his permissions".
* R. Kolstad -- 11/2/80
* modified: R. Essick January 1982, to clean up some signal processing
*
*/
#if defined(SIGCHLD)
static int kidpid; /* passed by kidwatch() */
static union wait kidstatus;
#endif defined(SIGCHLD)
#ifndef FASTFORK
dounix (linebuf, hisuid, ttymode)
char linebuf[];
#else
dounix (hisuid, ttymode, arg0, arg1, arg2, arg3, arg4)
char *arg0,
*arg1,
*arg2,
*arg3,
*arg4;
#endif
{
register pid,
forktry,
rpid;
int (*p) (),
(*q) (),
(*r) ();
#if defined(SIGTSTP)
int (*s) ();
#endif defined(SIGTSTP)
#if defined(SIGCHLD)
int (*t) ();
extern int watchkid (); /* catch stopped kids */
#endif defined(SIGCHLD)
int retcode;
if (ttymode)
ttystop (); /* give back to normal mode */
pid = 0; /* satisfy init conditions */
forktry = 0; /* init the counter */
while (pid <= 0 && ++forktry < FORKTRY)
{
if ((pid = fork ()) == 0)
{
uncatchem (); /* reset this process signals */
/* if user can get his hands on it */
if (hisuid) /* reset to him */
{
setuid (globuid); /* give him his uid */
setgid (getgid ()); /* his group */
}
for (rpid = 3; rpid < NOFILE; rpid++) /* close extra files */
close (rpid);
#ifndef FASTFORK
if (linebuf == 0)
execl (hisshell, hisshell, 0);
else
execl (DFLTSH, "sh", "-c", linebuf, 0);
printf ("Rats -- Couldn't load %s\n", DFLTSH);
#else
if (arg0 == 0)
execlp (hisshell, hisshell, 0);
else
execlp (arg0, arg0, arg1, arg2, arg3, arg4);
printf ("Rats - Couldn't load %s\n", arg0);
#endif
_exit (BAD); /* if exec fails .. */
}
if (pid <= 0) /* if fork failed */
sleep (2); /* wait a bit */
}
if (pid > 0) /* only if have son */
{
p = signal (SIGHUP, SIG_IGN);
q = signal (SIGINT, SIG_IGN);
r = signal (SIGQUIT, SIG_IGN);
#if defined(SIGTSTP)
s = signal (SIGTSTP, SIG_DFL);
#endif defined(SIGTSTP)
#if defined(SIGCHLD)
t = signal (SIGCHLD, watchkid); /* if he signals */
#endif defined(SIGCHLD)
while ((rpid = wait (&retcode)) != pid && rpid != -1);
if (rpid == -1)
{
#if defined(SIGCHLD)
/*
* watchkid() might have sucked down the status of the terminated
* child, so we load whatever value it left for us in kidstatus
* (provided that kidpid was ok)
*/
if (pid == kidpid) /* the one we wanted */
retcode = kidstatus.w_status; /* from watchkid() */
else
retcode = 1 << 8; /* make an error */
#else
/*
* normal case, if the wait() failed for some reason we say that it
* is an error.
*/
retcode = 1 << 8; /* indicates error */
#endif defined(SIGCHLD)
}
signal (SIGHUP, p);
signal (SIGINT, q);
signal (SIGQUIT, r);
#if defined(SIGTSTP)
signal (SIGTSTP, s);
#endif defined(SIGTSTP)
#if defined(SIGCHLD)
signal (SIGCHLD, t);
#endif defined(SIGCHLD)
}
else
retcode = -1; /* some sort of error */
if (ttymode)
ttystrt (); /* back into raw mode */
return retcode >> 8; /* hand him the completion code */
}
#if defined(SIGCHLD)
/*
* watchkid()
*
* called when we receive a SIGCHLD signal, indicating that a child's
* status has changed. This routine looks via wait3() to see if
* the children have merely been suspended. If so, it stops itself
* so that it's parent can decide what to do.
*
* This catches problems with programs like vi which run in raw mode
* and catch ^z as a character. They later try to signal the entire
* process group but are unable to signal the notes process since it
* has a different effective uid. By watching the SIGCHLD signal, we
* get notification when the vi process has stopped and we can stop
* ourselves.
* Ray Essick, Augst 22, 1984
*/
static int watchkid (sig)
int sig;
{
kidpid = wait3 (&kidstatus, WUNTRACED | WNOHANG, 0);/* get status */
if (kidpid == 0) /* nothing to report */
return 0; /* get out */
if (kidpid == (-1)) /* no children at all */
return 0; /* get out */
if (WIFSTOPPED (kidstatus)) /* stopped himself */
{
kill (getpid (), SIGTSTP); /* stop myself */
}
}
#endif defined(SIGCHLD)
/*
* If the condition is TRUE (non-zero) abort the program.
*
* Print the supplied message and halt.
* Leave an optional core dump for debugging later.
*
* Ray Essick 10/23/80
*/
x (cond, p) char *p;
{
if (cond == 0)
return; /* didnt fail */
perror ("notes");
fprintf (stderr, "Fatal Internal Notesfile Error: %s\n", p);
ttystop (); /* back to normal */
#ifdef DUMPCORE
if (chdir (Mstdir) < 0) /* go to known place */
exit (BAD); /* drop out */
if (chdir (UTILITY) < 0)
exit (BAD); /* drop out */
if (chdir (DUMPCORE) < 0) /* writeable to all */
exit (BAD); /* drop out */
#ifdef RUNSUID
setuid (globuid); /* won't dump if euid != uid */
#else
setgid (getgid ()); /* no gift groups */
#endif RUNSUID
#endif DUMPCORE
#ifdef NFMAINT
/*
* This code is kind of risky. If the NFMAINT notesfile ever
* gets trashed and starts calling this routine, look out because
* it will recursively fail. This is the unfortunate byproduct
* of the fact that the "x" routine doesn't know what the
* current notesfile is.
*/
{
char pbuf[512]; /* hold message */
char pbuf2[128]; /* and title */
char pbuf3[256]; /* core image */
char *tail; /* end of invocation */
sprintf (pbuf2, "%s: aborted", Invokedas);
sprintf (pbuf, "Program:\t%s\nMessage:\t%s\n\nerrno:\t\t%d (%s)\n",
Invokedas, p, errno, strerror(errno));
#ifdef DUMPCORE
if ((tail = rindex (Invokedas, '/')) == NULL) /* pathname? */
tail = Invokedas; /* simple invocation */
else
tail++; /* strip the slash */
sprintf (pbuf3, "%s/%s/%s/%s", Mstdir, UTILITY, DUMPCORE, tail);
nfabort (NFMAINT, pbuf, pbuf2, pbuf3, BAD); /* log & abort */
#else ! DUMPCORE
nfcomment (NFMAINT, pbuf, pbuf2, 0, 0); /* actual insertion */
#endif DUMPCORE
}
#endif NFMAINT
/*
* Handle the exit if NFMAINT is undefined.
*/
#ifdef DUMPCORE
abort (); /* dump in "core" */
#else
exit (BAD); /* for production */
#endif DUMPCORE
}
/*
* lock creates a lock file, or waits until it can create the lock.
* lock files are of the form lock# where # is a character passed
* to the routine.
*
* Rob Kolstad 10/20/80
* modified: rbe December 1981 to add full path name for lock file
*/
locknf (io, c)
struct io_f *io;
char c;
{
register int i,
holderr,
trys;
char p[WDLEN];
sprintf (p, "%s/%s/%c%s", Mstdir, LOCKS, c, io -> nf);
/* generate file name */
trys = LOCKTRY; /* set him up */
while ((i = creat (p, 0)) < 0)
{
if (trys-- == 0)
{
holderr = errno; /* before it's abused */
fprintf (stderr, "lock %c (%s) permanently locked - consult a guru\n",
c, io -> nf);
#ifdef NFMAINT
if (strcmp (NFMAINT, io -> nf)) /* avoid loops */
{
char pbuf[256]; /* for error logging */
char tbuf[256]; /* title */
sprintf (pbuf,
"lock %c failed for %s,\nerrno = %d (%s)\nProgram = %s\n",
c, io -> fullname, holderr, strerror(holderr),
Invokedas);
sprintf (tbuf, "%s: locked (%c)", io -> nf, c);
nfcomment (NFMAINT, pbuf, tbuf, 0, 0);
}
#endif NFMAINT
ttystop ();
exit (BAD);
}
sleep (2); /* guarantee at least 1 */
}
ignoresigs++; /* critical section */
/*
* could be above getting the lock, but wanted to be able to suspend
* while getting the lock. The interuptable window is very small
*/
close (i);
}
/*
* unlock takes the same arguements as the lock routine, and it
* will remove the corresponding lock file
*
* Rob Kolstad 10/20/80
* modified: rbe December 1981 to add full path name for lock name
*/
unlocknf (io, c)
struct io_f *io;
char c;
{
char p[WDLEN];
sprintf (p, "%s/%s/%c%s", Mstdir, LOCKS, c, io -> nf);
/* generate file name */
x (unlink (p) < 0, "unlock: unlink lock");
ignoresigs--; /* no longer critical */
}
/*
* glock creates a lock file, or waits until it can create the lock.
* lock files are of the form lock# where # is a character passed
* to the routine.
* This lock file is a GLOBAL lock - across all notefiles
*
* taken from lock routine above by R. Essick December 1981
*/
glocknf (io, c)
struct io_f *io; /* unused in this routine */
char c;
{
register int i,
holderr,
trys;
char p[WDLEN];
sprintf (p, "%s/%s/%c", Mstdir, LOCKS, c); /* generate file name */
trys = LOCKTRY;
while ((i = creat (p, 0)) < 0)
{
if (trys-- == 0)
{
holderr = errno; /* before it's abused */
fprintf (stderr, "lock%c combo lost - consult your local guru\n", c);
#ifdef NFMAINT
if (strcmp (NFMAINT, io -> nf)) /* don't loop on self */
{
char pbuf[256]; /* for error logging */
char pbuf2[256];
sprintf (pbuf,
"glock %c failed for %s, errno = %d (%s)\nProgram = %s\n",
c, io -> fullname, holderr, strerror(holderr),
Invokedas);
sprintf (pbuf2, "Frozen Global Lock (%c)", c);
nfcomment (NFMAINT, pbuf, pbuf2, 0, 0);
}
#endif NFMAINT
ttystop ();
exit (BAD);
}
sleep (2); /* is there a smaller time interval */
}
close (i);
}
/*
* gunlock takes the same arguements as the lock routine, and it
* will remove the corresponding lock file
* This is GLOBAL locking - across all notefiles
*
* copy of code from unlock, with minor changes
* Ray Essick December 1981
*/
gunlocknf (io, c)
struct io_f *io; /* not used by this routine */
char c;
{
char p[WDLEN];
sprintf (p, "%s/%s/%c", Mstdir, LOCKS, c); /* make the file name */
x (unlink (p) < 0, "gunlock: unlink lock");
}
/*
* length tells us max(length of string, 1)
*/
len (p, n) char *p;
{
int i;
i = n;
p += n;
while (*--p == ' ' && --i)
if (i == 0)
i = 1;
return i;
}
/*
* shell - give the user a shell
* this includes:
* 1) changing to the directory where he came in from
* 2) giving him a shell
* 3) return to the notefile directory
*
* original author: Ray Essick may 29, 1981
*
*/
gshell ()
{
printf ("\n");
#ifndef FASTFORK
dounix (0, 1, 1); /* give him his shell */
#else
dounix (1, 1, 0, 0, 0, 0, 0);
#endif
return 0;
}
/* copydate merely moves a when_f structure from 'from' to 'to' */
/* ray essick - 20-nov-1981 */
copydate (from, to)
struct when_f *from,
*to;
{
*to = *from; /* use block move */
}
/* strmove - copy a null terminated string to another */
/* returns the count of characters moved, this count includes the */
/* null terminator.. */
/* r. essick 20-nov-81 */
strmove (p, q)
char *p,
*q; /* from p to q */
{
int count;
register char *pp,
*qq;
count = 0; /* start with no characters moved */
pp = p;
qq = q; /* use registers for speed */
while (*qq++ = *pp++)
count++;
return count; /* return count of characters moved */
/* don't include the terminator */
}
/* copyauth(from, to) struct auth_f *from,*auth
* copys author from from to to
* Ray Essick December 1981
*
* SHOULD USE STRUCTURE ASSIGNMENT IN ALL PLACES THAT CALL THIS
*/
copyauth (from, to)
struct auth_f *from,
*to;
{
strncpy (to -> aname, from -> aname, NAMESZ); /* author name */
strncpy (to -> asystem, from -> asystem, HOMESYSSZ);/* home machine */
to -> aid = from -> aid; /* and user id */
}
/* listget, listconv - parse a list of numbers.
* this is all taken ( sort of ) from Rob Kolstad's getpg
* program
*/
listget (buf, ptr, start, finish)
char buf[];
int *ptr,
*start,
*finish;
{
if ((buf[*ptr] < '0' || buf[*ptr] > '9') && buf[*ptr] != ' ')
{
return 0; /* end of this list */
}
*start = listconv (buf, ptr); /* get the first */
*finish = *start; /* default to single */
if (buf[*ptr] == '-')
{
++(*ptr); /* trash that separator */
*finish = listconv (buf, ptr); /* grab second */
++(*ptr); /* bump past delimiter */
return 2; /* parsed 2 arguements */
}
else
{
if (buf[*ptr] != '\0')
++(*ptr); /* dump delimiter */
return 1;
}
}
listconv (buf, ptr)
char buf[];
int *ptr;
{
int i;
i = 0;
while (buf[*ptr] == ' ')
++(*ptr);
while (buf[*ptr] >= '0' && buf[*ptr] <= '9')
{
i = 10 * i + buf[*ptr] - '0';
++(*ptr); /* bump him */
}
return (i);
}
/* tolcase - check to see if upper case, and convert to lcase */
/* R. Essick Feb 1982 */
tolcase (c)
char c;
{
if (isascii (c) && isupper (c))
return (tolower (c)); /* to lower case */
return (c); /* leave as is */
}
/*
* Date printing stuff.
*
* CHANGE TO CTIME(III) FORMAT EVENTUALLY
*/
char *mnames[13] = /* so indexes work right */
{
"???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"
};
sprdate (w, str) struct when_f *w;
char *str;
{
char *m;
int h,
i,
j; /* temps to print 0's or funny strings */
m = "am";
h = w -> w_hours;
if (h >= 12)
m = "pm";
if (h == 0)
h = 12;
if (h > 12)
h -= 12;
i = w -> w_mins / 10;
j = w -> w_mins % 10; /* get those leading zeroes */
sprintf (str, "%2d:%d%d %2s %3s %2d, %4d", h, i, j, m, mnames[w -> w_month], w -> w_day, w -> w_year);
/* sprintf puts it into a string */
}
prdate (zdate) struct when_f *zdate;
{
char line[DATELEN];
sprdate (zdate, line); /* format it */
printf ("%s", line); /* and print it */
}
/*
* Saves a string with malloc() and returns a pointer
* to where it wound up. Useful for building lists of
* stuff.
*
* Courtesy of Lou Salkind & Rick Spickelmier.
*/
char *strsave (s)
char *s;
{
char *p;
extern char *malloc ();
p = malloc (strlen (s) + 1);
strcpy (p, s);
return (p);
}
/*
* substr(a,b) see if A is a substring of B
*
* uses: strlen.
*/
substr (a, b)
char *a;
char *b;
{
register char first;
register int length; /* length of a */
register int count; /* max checks */
first = *a; /* get first */
length = strlen (a); /* for strncmp */
count = strlen (b) - length + 1; /* max checks */
while (count-- > 0) /* can try */
{
if (*b == first && !strncasecmp (a, b, length))
return (1); /* is a substring */
b++; /* on to next */
}
return (0); /* not a substring */
}
/*
* routine to process a string and remove any
* nasties like control characters and escape codes.
* also zips newlines.
*/
int strclean (p)
char *p;
{
if (p == (char *) NULL)
return 0;
if (*p == '\0')
return 0;
do
{
if (!isascii (*p) || iscntrl (*p))
*p = '_'; /* kill controls */
if (*p == '\n') /* hate newlines */
*p = '_';
} while (*++p != '\0');
return (0);
}