1: #include <errno.h> 2: #include "parms.h" 3: #include "structs.h" 4: #include <signal.h> /* signal processing */ 5: #include <ctype.h> 6: #ifdef SIGCHLD 7: #include <sys/wait.h> /* for child status */ 8: #endif SIGCHLD 9: 10: #ifdef RCSIDENT 11: static char rcsid[] = "$Header: src/usenet/notes/src/RCS/misc.c,v 1.9.1 96/3/21 21:49:42 paul Exp $"; 12: #endif RCSIDENT 13: 14: #define LOCKTRY 10 /* number of shots at grabbing */ 15: #define FORKTRY 10 /* tries at forking */ 16: 17: /* 18: * dounix(charstring, flag) will execute that character string as a shell command 19: * stolen from shell, though this one catches more signals. 20: * 21: * Depending on the RUNSUID flag the routine sets things back to 22: * the users group or uid. Early versions were setuid and newer 23: * versions only run setgid. Don't get confused by the "hisuid" 24: * argument: it really means "reset to his permissions". 25: * R. Kolstad -- 11/2/80 26: * modified: R. Essick January 1982, to clean up some signal processing 27: * 28: */ 29: 30: #if defined(SIGCHLD) 31: static int kidpid; /* passed by kidwatch() */ 32: static union wait kidstatus; 33: #endif defined(SIGCHLD) 34: 35: 36: #ifndef FASTFORK 37: dounix (linebuf, hisuid, ttymode) 38: char linebuf[]; 39: #else 40: dounix (hisuid, ttymode, arg0, arg1, arg2, arg3, arg4) 41: char *arg0, 42: *arg1, 43: *arg2, 44: *arg3, 45: *arg4; 46: #endif 47: { 48: register pid, 49: forktry, 50: rpid; 51: int (*p) (), 52: (*q) (), 53: (*r) (); 54: #if defined(SIGTSTP) 55: int (*s) (); 56: #endif defined(SIGTSTP) 57: #if defined(SIGCHLD) 58: int (*t) (); 59: extern int watchkid (); /* catch stopped kids */ 60: #endif defined(SIGCHLD) 61: int retcode; 62: 63: 64: if (ttymode) 65: ttystop (); /* give back to normal mode */ 66: pid = 0; /* satisfy init conditions */ 67: forktry = 0; /* init the counter */ 68: while (pid <= 0 && ++forktry < FORKTRY) 69: { 70: if ((pid = fork ()) == 0) 71: { 72: uncatchem (); /* reset this process signals */ 73: /* if user can get his hands on it */ 74: if (hisuid) /* reset to him */ 75: { 76: setuid (globuid); /* give him his uid */ 77: setgid (getgid ()); /* his group */ 78: } 79: for (rpid = 3; rpid < NOFILE; rpid++) /* close extra files */ 80: close (rpid); 81: 82: #ifndef FASTFORK 83: if (linebuf == 0) 84: execl (hisshell, hisshell, 0); 85: else 86: execl (DFLTSH, "sh", "-c", linebuf, 0); 87: printf ("Rats -- Couldn't load %s\n", DFLTSH); 88: #else 89: if (arg0 == 0) 90: execlp (hisshell, hisshell, 0); 91: else 92: execlp (arg0, arg0, arg1, arg2, arg3, arg4); 93: printf ("Rats - Couldn't load %s\n", arg0); 94: #endif 95: 96: _exit (BAD); /* if exec fails .. */ 97: } 98: if (pid <= 0) /* if fork failed */ 99: sleep (2); /* wait a bit */ 100: } 101: if (pid > 0) /* only if have son */ 102: { 103: p = signal (SIGHUP, SIG_IGN); 104: q = signal (SIGINT, SIG_IGN); 105: r = signal (SIGQUIT, SIG_IGN); 106: #if defined(SIGTSTP) 107: s = signal (SIGTSTP, SIG_DFL); 108: #endif defined(SIGTSTP) 109: #if defined(SIGCHLD) 110: t = signal (SIGCHLD, watchkid); /* if he signals */ 111: #endif defined(SIGCHLD) 112: while ((rpid = wait (&retcode)) != pid && rpid != -1); 113: if (rpid == -1) 114: { 115: #if defined(SIGCHLD) 116: /* 117: * watchkid() might have sucked down the status of the terminated 118: * child, so we load whatever value it left for us in kidstatus 119: * (provided that kidpid was ok) 120: */ 121: if (pid == kidpid) /* the one we wanted */ 122: retcode = kidstatus.w_status; /* from watchkid() */ 123: else 124: retcode = 1 << 8; /* make an error */ 125: #else 126: /* 127: * normal case, if the wait() failed for some reason we say that it 128: * is an error. 129: */ 130: retcode = 1 << 8; /* indicates error */ 131: #endif defined(SIGCHLD) 132: } 133: signal (SIGHUP, p); 134: signal (SIGINT, q); 135: signal (SIGQUIT, r); 136: #if defined(SIGTSTP) 137: signal (SIGTSTP, s); 138: #endif defined(SIGTSTP) 139: #if defined(SIGCHLD) 140: signal (SIGCHLD, t); 141: #endif defined(SIGCHLD) 142: } 143: else 144: retcode = -1; /* some sort of error */ 145: if (ttymode) 146: ttystrt (); /* back into raw mode */ 147: return retcode >> 8; /* hand him the completion code */ 148: } 149: 150: #if defined(SIGCHLD) 151: /* 152: * watchkid() 153: * 154: * called when we receive a SIGCHLD signal, indicating that a child's 155: * status has changed. This routine looks via wait3() to see if 156: * the children have merely been suspended. If so, it stops itself 157: * so that it's parent can decide what to do. 158: * 159: * This catches problems with programs like vi which run in raw mode 160: * and catch ^z as a character. They later try to signal the entire 161: * process group but are unable to signal the notes process since it 162: * has a different effective uid. By watching the SIGCHLD signal, we 163: * get notification when the vi process has stopped and we can stop 164: * ourselves. 165: * Ray Essick, Augst 22, 1984 166: */ 167: static int watchkid (sig) 168: int sig; 169: { 170: 171: kidpid = wait3 (&kidstatus, WUNTRACED | WNOHANG, 0);/* get status */ 172: if (kidpid == 0) /* nothing to report */ 173: return 0; /* get out */ 174: if (kidpid == (-1)) /* no children at all */ 175: return 0; /* get out */ 176: if (WIFSTOPPED (kidstatus)) /* stopped himself */ 177: { 178: kill (getpid (), SIGTSTP); /* stop myself */ 179: } 180: } 181: #endif defined(SIGCHLD) 182: 183: /* 184: * If the condition is TRUE (non-zero) abort the program. 185: * 186: * Print the supplied message and halt. 187: * Leave an optional core dump for debugging later. 188: * 189: * Ray Essick 10/23/80 190: */ 191: 192: x (cond, p) char *p; 193: { 194: if (cond == 0) 195: return; /* didnt fail */ 196: 197: perror ("notes"); 198: fprintf (stderr, "Fatal Internal Notesfile Error: %s\n", p); 199: ttystop (); /* back to normal */ 200: 201: 202: #ifdef DUMPCORE 203: if (chdir (Mstdir) < 0) /* go to known place */ 204: exit (BAD); /* drop out */ 205: if (chdir (UTILITY) < 0) 206: exit (BAD); /* drop out */ 207: if (chdir (DUMPCORE) < 0) /* writeable to all */ 208: exit (BAD); /* drop out */ 209: #ifdef RUNSUID 210: setuid (globuid); /* won't dump if euid != uid */ 211: #else 212: setgid (getgid ()); /* no gift groups */ 213: #endif RUNSUID 214: #endif DUMPCORE 215: 216: #ifdef NFMAINT 217: /* 218: * This code is kind of risky. If the NFMAINT notesfile ever 219: * gets trashed and starts calling this routine, look out because 220: * it will recursively fail. This is the unfortunate byproduct 221: * of the fact that the "x" routine doesn't know what the 222: * current notesfile is. 223: */ 224: { 225: char pbuf[512]; /* hold message */ 226: char pbuf2[128]; /* and title */ 227: char pbuf3[256]; /* core image */ 228: char *tail; /* end of invocation */ 229: 230: sprintf (pbuf2, "%s: aborted", Invokedas); 231: sprintf (pbuf, "Program:\t%s\nMessage:\t%s\n\nerrno:\t\t%d (%s)\n", 232: Invokedas, p, errno, strerror(errno)); 233: #ifdef DUMPCORE 234: if ((tail = rindex (Invokedas, '/')) == NULL) /* pathname? */ 235: tail = Invokedas; /* simple invocation */ 236: else 237: tail++; /* strip the slash */ 238: sprintf (pbuf3, "%s/%s/%s/%s", Mstdir, UTILITY, DUMPCORE, tail); 239: nfabort (NFMAINT, pbuf, pbuf2, pbuf3, BAD); /* log & abort */ 240: #else ! DUMPCORE 241: nfcomment (NFMAINT, pbuf, pbuf2, 0, 0); /* actual insertion */ 242: #endif DUMPCORE 243: } 244: #endif NFMAINT 245: 246: /* 247: * Handle the exit if NFMAINT is undefined. 248: */ 249: 250: #ifdef DUMPCORE 251: abort (); /* dump in "core" */ 252: #else 253: exit (BAD); /* for production */ 254: #endif DUMPCORE 255: } 256: 257: /* 258: * lock creates a lock file, or waits until it can create the lock. 259: * lock files are of the form lock# where # is a character passed 260: * to the routine. 261: * 262: * Rob Kolstad 10/20/80 263: * modified: rbe December 1981 to add full path name for lock file 264: */ 265: 266: locknf (io, c) 267: struct io_f *io; 268: char c; 269: { 270: register int i, 271: holderr, 272: trys; 273: char p[WDLEN]; 274: 275: sprintf (p, "%s/%s/%c%s", Mstdir, LOCKS, c, io -> nf); 276: /* generate file name */ 277: trys = LOCKTRY; /* set him up */ 278: while ((i = creat (p, 0)) < 0) 279: { 280: if (trys-- == 0) 281: { 282: holderr = errno; /* before it's abused */ 283: fprintf (stderr, "lock %c (%s) permanently locked - consult a guru\n", 284: c, io -> nf); 285: #ifdef NFMAINT 286: if (strcmp (NFMAINT, io -> nf)) /* avoid loops */ 287: { 288: char pbuf[256]; /* for error logging */ 289: char tbuf[256]; /* title */ 290: sprintf (pbuf, 291: "lock %c failed for %s,\nerrno = %d (%s)\nProgram = %s\n", 292: c, io -> fullname, holderr, strerror(holderr), 293: Invokedas); 294: sprintf (tbuf, "%s: locked (%c)", io -> nf, c); 295: nfcomment (NFMAINT, pbuf, tbuf, 0, 0); 296: } 297: #endif NFMAINT 298: ttystop (); 299: exit (BAD); 300: } 301: sleep (2); /* guarantee at least 1 */ 302: } 303: ignoresigs++; /* critical section */ 304: /* 305: * could be above getting the lock, but wanted to be able to suspend 306: * while getting the lock. The interuptable window is very small 307: */ 308: close (i); 309: } 310: 311: /* 312: * unlock takes the same arguements as the lock routine, and it 313: * will remove the corresponding lock file 314: * 315: * Rob Kolstad 10/20/80 316: * modified: rbe December 1981 to add full path name for lock name 317: */ 318: 319: unlocknf (io, c) 320: struct io_f *io; 321: char c; 322: { 323: char p[WDLEN]; 324: 325: sprintf (p, "%s/%s/%c%s", Mstdir, LOCKS, c, io -> nf); 326: /* generate file name */ 327: x (unlink (p) < 0, "unlock: unlink lock"); 328: ignoresigs--; /* no longer critical */ 329: } 330: 331: /* 332: * glock creates a lock file, or waits until it can create the lock. 333: * lock files are of the form lock# where # is a character passed 334: * to the routine. 335: * This lock file is a GLOBAL lock - across all notefiles 336: * 337: * taken from lock routine above by R. Essick December 1981 338: */ 339: 340: glocknf (io, c) 341: struct io_f *io; /* unused in this routine */ 342: char c; 343: { 344: register int i, 345: holderr, 346: trys; 347: char p[WDLEN]; 348: 349: sprintf (p, "%s/%s/%c", Mstdir, LOCKS, c); /* generate file name */ 350: trys = LOCKTRY; 351: while ((i = creat (p, 0)) < 0) 352: { 353: if (trys-- == 0) 354: { 355: holderr = errno; /* before it's abused */ 356: fprintf (stderr, "lock%c combo lost - consult your local guru\n", c); 357: #ifdef NFMAINT 358: if (strcmp (NFMAINT, io -> nf)) /* don't loop on self */ 359: { 360: char pbuf[256]; /* for error logging */ 361: char pbuf2[256]; 362: sprintf (pbuf, 363: "glock %c failed for %s, errno = %d (%s)\nProgram = %s\n", 364: c, io -> fullname, holderr, strerror(holderr), 365: Invokedas); 366: sprintf (pbuf2, "Frozen Global Lock (%c)", c); 367: nfcomment (NFMAINT, pbuf, pbuf2, 0, 0); 368: } 369: #endif NFMAINT 370: ttystop (); 371: exit (BAD); 372: } 373: sleep (2); /* is there a smaller time interval */ 374: } 375: close (i); 376: } 377: 378: /* 379: * gunlock takes the same arguements as the lock routine, and it 380: * will remove the corresponding lock file 381: * This is GLOBAL locking - across all notefiles 382: * 383: * copy of code from unlock, with minor changes 384: * Ray Essick December 1981 385: */ 386: 387: gunlocknf (io, c) 388: struct io_f *io; /* not used by this routine */ 389: char c; 390: { 391: char p[WDLEN]; 392: 393: sprintf (p, "%s/%s/%c", Mstdir, LOCKS, c); /* make the file name */ 394: x (unlink (p) < 0, "gunlock: unlink lock"); 395: } 396: 397: /* 398: * length tells us max(length of string, 1) 399: */ 400: len (p, n) char *p; 401: { 402: int i; 403: i = n; 404: p += n; 405: while (*--p == ' ' && --i) 406: if (i == 0) 407: i = 1; 408: return i; 409: } 410: 411: /* 412: * shell - give the user a shell 413: * this includes: 414: * 1) changing to the directory where he came in from 415: * 2) giving him a shell 416: * 3) return to the notefile directory 417: * 418: * original author: Ray Essick may 29, 1981 419: * 420: */ 421: 422: gshell () 423: { 424: printf ("\n"); 425: #ifndef FASTFORK 426: dounix (0, 1, 1); /* give him his shell */ 427: #else 428: dounix (1, 1, 0, 0, 0, 0, 0); 429: #endif 430: return 0; 431: } 432: 433: /* copydate merely moves a when_f structure from 'from' to 'to' */ 434: /* ray essick - 20-nov-1981 */ 435: 436: copydate (from, to) 437: struct when_f *from, 438: *to; 439: { 440: *to = *from; /* use block move */ 441: } 442: 443: /* strmove - copy a null terminated string to another */ 444: /* returns the count of characters moved, this count includes the */ 445: /* null terminator.. */ 446: /* r. essick 20-nov-81 */ 447: 448: strmove (p, q) 449: char *p, 450: *q; /* from p to q */ 451: { 452: int count; 453: register char *pp, 454: *qq; 455: 456: count = 0; /* start with no characters moved */ 457: pp = p; 458: qq = q; /* use registers for speed */ 459: while (*qq++ = *pp++) 460: count++; 461: return count; /* return count of characters moved */ 462: /* don't include the terminator */ 463: } 464: 465: /* copyauth(from, to) struct auth_f *from,*auth 466: * copys author from from to to 467: * Ray Essick December 1981 468: * 469: * SHOULD USE STRUCTURE ASSIGNMENT IN ALL PLACES THAT CALL THIS 470: */ 471: copyauth (from, to) 472: struct auth_f *from, 473: *to; 474: { 475: 476: strncpy (to -> aname, from -> aname, NAMESZ); /* author name */ 477: strncpy (to -> asystem, from -> asystem, HOMESYSSZ);/* home machine */ 478: to -> aid = from -> aid; /* and user id */ 479: } 480: 481: /* listget, listconv - parse a list of numbers. 482: * this is all taken ( sort of ) from Rob Kolstad's getpg 483: * program 484: */ 485: 486: listget (buf, ptr, start, finish) 487: char buf[]; 488: int *ptr, 489: *start, 490: *finish; 491: { 492: if ((buf[*ptr] < '0' || buf[*ptr] > '9') && buf[*ptr] != ' ') 493: { 494: return 0; /* end of this list */ 495: } 496: *start = listconv (buf, ptr); /* get the first */ 497: *finish = *start; /* default to single */ 498: if (buf[*ptr] == '-') 499: { 500: ++(*ptr); /* trash that separator */ 501: *finish = listconv (buf, ptr); /* grab second */ 502: ++(*ptr); /* bump past delimiter */ 503: return 2; /* parsed 2 arguements */ 504: } 505: else 506: { 507: if (buf[*ptr] != '\0') 508: ++(*ptr); /* dump delimiter */ 509: return 1; 510: } 511: } 512: 513: listconv (buf, ptr) 514: char buf[]; 515: int *ptr; 516: { 517: int i; 518: i = 0; 519: while (buf[*ptr] == ' ') 520: ++(*ptr); 521: while (buf[*ptr] >= '0' && buf[*ptr] <= '9') 522: { 523: i = 10 * i + buf[*ptr] - '0'; 524: ++(*ptr); /* bump him */ 525: } 526: return (i); 527: } 528: 529: /* tolcase - check to see if upper case, and convert to lcase */ 530: /* R. Essick Feb 1982 */ 531: tolcase (c) 532: char c; 533: { 534: if (isascii (c) && isupper (c)) 535: return (tolower (c)); /* to lower case */ 536: return (c); /* leave as is */ 537: } 538: 539: /* 540: * Date printing stuff. 541: * 542: * CHANGE TO CTIME(III) FORMAT EVENTUALLY 543: */ 544: 545: char *mnames[13] = /* so indexes work right */ 546: { 547: "???", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", 548: "Sep", "Oct", "Nov", "Dec" 549: }; 550: 551: sprdate (w, str) struct when_f *w; 552: char *str; 553: { 554: char *m; 555: int h, 556: i, 557: j; /* temps to print 0's or funny strings */ 558: m = "am"; 559: h = w -> w_hours; 560: if (h >= 12) 561: m = "pm"; 562: if (h == 0) 563: h = 12; 564: if (h > 12) 565: h -= 12; 566: i = w -> w_mins / 10; 567: j = w -> w_mins % 10; /* get those leading zeroes */ 568: sprintf (str, "%2d:%d%d %2s %3s %2d, %4d", h, i, j, m, mnames[w -> w_month], w -> w_day, w -> w_year); 569: /* sprintf puts it into a string */ 570: } 571: 572: prdate (zdate) struct when_f *zdate; 573: { 574: char line[DATELEN]; 575: 576: sprdate (zdate, line); /* format it */ 577: printf ("%s", line); /* and print it */ 578: } 579: 580: /* 581: * Saves a string with malloc() and returns a pointer 582: * to where it wound up. Useful for building lists of 583: * stuff. 584: * 585: * Courtesy of Lou Salkind & Rick Spickelmier. 586: */ 587: 588: 589: char *strsave (s) 590: char *s; 591: { 592: char *p; 593: extern char *malloc (); 594: 595: p = malloc (strlen (s) + 1); 596: strcpy (p, s); 597: return (p); 598: } 599: 600: /* 601: * substr(a,b) see if A is a substring of B 602: * 603: * uses: strlen. 604: */ 605: 606: substr (a, b) 607: char *a; 608: char *b; 609: { 610: register char first; 611: register int length; /* length of a */ 612: register int count; /* max checks */ 613: 614: first = *a; /* get first */ 615: length = strlen (a); /* for strncmp */ 616: count = strlen (b) - length + 1; /* max checks */ 617: while (count-- > 0) /* can try */ 618: { 619: if (*b == first && !strncasecmp (a, b, length)) 620: return (1); /* is a substring */ 621: b++; /* on to next */ 622: } 623: return (0); /* not a substring */ 624: } 625: 626: /* 627: * routine to process a string and remove any 628: * nasties like control characters and escape codes. 629: * also zips newlines. 630: */ 631: 632: int strclean (p) 633: char *p; 634: { 635: if (p == (char *) NULL) 636: return 0; 637: if (*p == '\0') 638: return 0; 639: do 640: { 641: if (!isascii (*p) || iscntrl (*p)) 642: *p = '_'; /* kill controls */ 643: if (*p == '\n') /* hate newlines */ 644: *p = '_'; 645: } while (*++p != '\0'); 646: return (0); 647: }