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