121135Sdist /* 2*28801Skarels * Copyright (c) 1980,1986 Regents of the University of California. 321135Sdist * All rights reserved. The Berkeley software License Agreement 421135Sdist * specifies the terms and conditions for redistribution. 521135Sdist */ 621135Sdist 712682Ssam #ifndef lint 8*28801Skarels static char sccsid[] = "@(#)init.c 5.6 (Berkeley) 05/26/86"; 921135Sdist #endif not lint 1012682Ssam 111029Sbill #include <signal.h> 121029Sbill #include <sys/types.h> 131029Sbill #include <utmp.h> 141029Sbill #include <setjmp.h> 151403Sbill #include <sys/reboot.h> 162821Swnj #include <errno.h> 1713021Ssam #include <sys/file.h> 1816452Sroot #include <ttyent.h> 19*28801Skarels #include <sys/syslog.h> 2023147Sbloom #include <sys/stat.h> 211029Sbill 221029Sbill #define LINSIZ sizeof(wtmp.ut_line) 2324494Skarels #define CMDSIZ 200 /* max string length for getty or window command*/ 2423147Sbloom #define ALL p = itab; p ; p = p->next 251029Sbill #define EVER ;; 261029Sbill #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 271029Sbill #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 281029Sbill 291029Sbill char shell[] = "/bin/sh"; 301029Sbill char minus[] = "-"; 311029Sbill char runc[] = "/etc/rc"; 3223147Sbloom char utmpf[] = "/etc/utmp"; 331029Sbill char wtmpf[] = "/usr/adm/wtmp"; 341029Sbill char ctty[] = "/dev/console"; 351029Sbill 361029Sbill struct utmp wtmp; 371029Sbill struct tab 381029Sbill { 391029Sbill char line[LINSIZ]; 4018542Sralph char comn[CMDSIZ]; 411029Sbill char xflag; 421029Sbill int pid; 4318542Sralph int wpid; /* window system pid for SIGHUP */ 4418542Sralph char wcmd[CMDSIZ]; /* command to start window system process */ 455971Sroot time_t gettytime; 465971Sroot int gettycnt; 4722181Skarels time_t windtime; 4822181Skarels int windcnt; 4923147Sbloom struct tab *next; 5023147Sbloom } *itab; 511029Sbill 521029Sbill int fi; 531029Sbill int mergflag; 541029Sbill char tty[20]; 551429Sbill jmp_buf sjbuf, shutpass; 562821Swnj time_t time0; 571029Sbill 581029Sbill int reset(); 592821Swnj int idle(); 601029Sbill char *strcpy(), *strcat(); 611029Sbill long lseek(); 621029Sbill 6318542Sralph struct sigvec rvec = { reset, sigmask(SIGHUP), 0 }; 6413021Ssam 6523147Sbloom 6613021Ssam #ifdef vax 671029Sbill main() 681029Sbill { 691403Sbill register int r11; /* passed thru from boot */ 7013021Ssam #else 719869Spugs main(argc, argv) 729869Spugs char **argv; 739869Spugs { 7413021Ssam #endif 751403Sbill int howto, oldhowto; 761403Sbill 772821Swnj time0 = time(0); 7813021Ssam #ifdef vax 791403Sbill howto = r11; 8013021Ssam #else 819869Spugs if (argc > 1 && argv[1][0] == '-') { 829869Spugs char *cp; 839869Spugs 849869Spugs howto = 0; 859869Spugs cp = &argv[1][1]; 869869Spugs while (*cp) switch (*cp++) { 879869Spugs case 'a': 889869Spugs howto |= RB_ASKNAME; 899869Spugs break; 909869Spugs case 's': 919869Spugs howto |= RB_SINGLE; 929869Spugs break; 939869Spugs } 949869Spugs } else { 959869Spugs howto = RB_SINGLE; 969869Spugs } 9713021Ssam #endif 9824854Seric openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 9913021Ssam sigvec(SIGTERM, &rvec, (struct sigvec *)0); 1002821Swnj signal(SIGTSTP, idle); 1011029Sbill signal(SIGSTOP, SIG_IGN); 1021029Sbill signal(SIGTTIN, SIG_IGN); 1031029Sbill signal(SIGTTOU, SIG_IGN); 10413021Ssam (void) setjmp(sjbuf); 10513021Ssam for (EVER) { 1061403Sbill oldhowto = howto; 1071403Sbill howto = RB_SINGLE; 1081429Sbill if (setjmp(shutpass) == 0) 1091429Sbill shutdown(); 1101403Sbill if (oldhowto & RB_SINGLE) 1111403Sbill single(); 1121403Sbill if (runcom(oldhowto) == 0) 1131403Sbill continue; 1141029Sbill merge(); 1151029Sbill multiple(); 1161029Sbill } 1171029Sbill } 1181029Sbill 1191429Sbill int shutreset(); 1201429Sbill 1211029Sbill shutdown() 1221029Sbill { 1231029Sbill register i; 12423147Sbloom register struct tab *p, *p1; 1251029Sbill 12623147Sbloom close(creat(utmpf, 0644)); 1271029Sbill signal(SIGHUP, SIG_IGN); 12823147Sbloom for (p = itab; p ; ) { 1291029Sbill term(p); 13023147Sbloom p1 = p->next; 13123147Sbloom free(p); 13223147Sbloom p = p1; 1331029Sbill } 13423147Sbloom itab = (struct tab *)0; 1351429Sbill signal(SIGALRM, shutreset); 136*28801Skarels (void) kill(-1, SIGTERM); /* one chance to catch it */ 137*28801Skarels sleep(5); 1381429Sbill alarm(30); 13913021Ssam for (i = 0; i < 5; i++) 1401029Sbill kill(-1, SIGKILL); 14113021Ssam while (wait((int *)0) != -1) 1421029Sbill ; 1431029Sbill alarm(0); 1441429Sbill shutend(); 1451429Sbill } 1461429Sbill 1471429Sbill char shutfailm[] = "WARNING: Something is hung (wont die); ps axl advised\n"; 1481429Sbill 1491429Sbill shutreset() 1501429Sbill { 1511429Sbill int status; 1521429Sbill 1531429Sbill if (fork() == 0) { 1541429Sbill int ct = open(ctty, 1); 1551429Sbill write(ct, shutfailm, sizeof (shutfailm)); 1561429Sbill sleep(5); 1571429Sbill exit(1); 1581429Sbill } 1591429Sbill sleep(5); 1601429Sbill shutend(); 1611429Sbill longjmp(shutpass, 1); 1621429Sbill } 1631429Sbill 1641429Sbill shutend() 1651429Sbill { 1662821Swnj register i, f; 1671429Sbill 1682821Swnj acct(0); 1691029Sbill signal(SIGALRM, SIG_DFL); 17013021Ssam for (i = 0; i < 10; i++) 1711029Sbill close(i); 17213021Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 1732821Swnj if (f >= 0) { 1742821Swnj SCPYN(wtmp.ut_line, "~"); 1752821Swnj SCPYN(wtmp.ut_name, "shutdown"); 17612682Ssam SCPYN(wtmp.ut_host, ""); 1772821Swnj time(&wtmp.ut_time); 1782821Swnj write(f, (char *)&wtmp, sizeof(wtmp)); 1792821Swnj close(f); 1802821Swnj } 18113021Ssam return (1); 1821029Sbill } 1831029Sbill 1841029Sbill single() 1851029Sbill { 1861029Sbill register pid; 1872821Swnj register xpid; 1882821Swnj extern errno; 1891029Sbill 19013021Ssam do { 19113021Ssam pid = fork(); 19213021Ssam if (pid == 0) { 19313021Ssam signal(SIGTERM, SIG_DFL); 19413021Ssam signal(SIGHUP, SIG_DFL); 19513021Ssam signal(SIGALRM, SIG_DFL); 19616452Sroot signal(SIGTSTP, SIG_IGN); 19713021Ssam (void) open(ctty, O_RDWR); 19813021Ssam dup2(0, 1); 19913021Ssam dup2(0, 2); 20013021Ssam execl(shell, minus, (char *)0); 20113021Ssam exit(0); 20213021Ssam } 20313021Ssam while ((xpid = wait((int *)0)) != pid) 20413021Ssam if (xpid == -1 && errno == ECHILD) 20513021Ssam break; 20613021Ssam } while (xpid == -1); 2071029Sbill } 2081029Sbill 2091403Sbill runcom(oldhowto) 2101403Sbill int oldhowto; 2111029Sbill { 2121029Sbill register pid, f; 2131403Sbill int status; 2141029Sbill 2151029Sbill pid = fork(); 21613021Ssam if (pid == 0) { 21713021Ssam (void) open("/", O_RDONLY); 21813021Ssam dup2(0, 1); 21913021Ssam dup2(0, 2); 2201403Sbill if (oldhowto & RB_SINGLE) 2211403Sbill execl(shell, shell, runc, (char *)0); 2221403Sbill else 2231403Sbill execl(shell, shell, runc, "autoboot", (char *)0); 2241403Sbill exit(1); 2251029Sbill } 22613021Ssam while (wait(&status) != pid) 2271029Sbill ; 22813021Ssam if (status) 22913021Ssam return (0); 23013021Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 2311029Sbill if (f >= 0) { 2321029Sbill SCPYN(wtmp.ut_line, "~"); 2331029Sbill SCPYN(wtmp.ut_name, "reboot"); 23412682Ssam SCPYN(wtmp.ut_host, ""); 2352821Swnj if (time0) { 2362821Swnj wtmp.ut_time = time0; 2372821Swnj time0 = 0; 2382821Swnj } else 2392821Swnj time(&wtmp.ut_time); 2401029Sbill write(f, (char *)&wtmp, sizeof(wtmp)); 2411029Sbill close(f); 2421029Sbill } 24313021Ssam return (1); 2441029Sbill } 2451029Sbill 24618542Sralph struct sigvec mvec = { merge, sigmask(SIGTERM), 0 }; 24713021Ssam /* 24813021Ssam * Multi-user. Listen for users leaving, SIGHUP's 24913021Ssam * which indicate ttys has changed, and SIGTERM's which 25013021Ssam * are used to shutdown the system. 25113021Ssam */ 2521029Sbill multiple() 2531029Sbill { 2541029Sbill register struct tab *p; 2551029Sbill register pid; 25623147Sbloom int omask; 2571029Sbill 25813021Ssam sigvec(SIGHUP, &mvec, (struct sigvec *)0); 25913021Ssam for (EVER) { 2601029Sbill pid = wait((int *)0); 26113021Ssam if (pid == -1) 2621029Sbill return; 26323147Sbloom omask = sigblock(SIGHUP); 26418542Sralph for (ALL) { 26518542Sralph /* must restart window system BEFORE emulator */ 26618542Sralph if (p->wpid == pid || p->wpid == -1) 26718542Sralph wstart(p); 26813021Ssam if (p->pid == pid || p->pid == -1) { 26918542Sralph /* disown the window system */ 27018542Sralph if (p->wpid) 27118542Sralph kill(p->wpid, SIGHUP); 2721029Sbill rmut(p); 2731029Sbill dfork(p); 2741029Sbill } 27518542Sralph } 27623147Sbloom sigsetmask(omask); 2771029Sbill } 2781029Sbill } 2791029Sbill 28013021Ssam /* 28113021Ssam * Merge current contents of ttys file 28213021Ssam * into in-core table of configured tty lines. 28313021Ssam * Entered as signal handler for SIGHUP. 28413021Ssam */ 28513021Ssam #define FOUND 1 28613021Ssam #define CHANGE 2 28718542Sralph #define WCHANGE 4 28813021Ssam 28913021Ssam merge() 29013021Ssam { 29113021Ssam register struct tab *p; 29216452Sroot register struct ttyent *t; 29323147Sbloom register struct tab *p1; 29413021Ssam 29513021Ssam for (ALL) 29613021Ssam p->xflag = 0; 29716452Sroot setttyent(); 29816452Sroot while (t = getttyent()) { 29916452Sroot if ((t->ty_status & TTY_ON) == 0) 30016452Sroot continue; 30113021Ssam for (ALL) { 30216452Sroot if (SCMPN(p->line, t->ty_name)) 30313021Ssam continue; 30413021Ssam p->xflag |= FOUND; 30516452Sroot if (SCMPN(p->comn, t->ty_getty)) { 30613021Ssam p->xflag |= CHANGE; 30716452Sroot SCPYN(p->comn, t->ty_getty); 30813021Ssam } 30918542Sralph if (SCMPN(p->wcmd, t->ty_window)) { 31018542Sralph p->xflag |= WCHANGE|CHANGE; 31118542Sralph SCPYN(p->wcmd, t->ty_window); 31218542Sralph } 31313021Ssam goto contin1; 31413021Ssam } 31518542Sralph 31623147Sbloom /* 31723147Sbloom * Make space for a new one 31823147Sbloom */ 31923147Sbloom p1 = (struct tab *)calloc(1, sizeof(*p1)); 32023147Sbloom if (!p1) { 32123147Sbloom syslog(LOG_ERR, "no space for '%s' !?!", t->ty_name); 32213021Ssam goto contin1; 32313021Ssam } 32423147Sbloom /* 32523147Sbloom * Put new terminal at the end of the linked list. 32623147Sbloom */ 32723147Sbloom if (itab) { 32823147Sbloom for (p = itab; p->next ; p = p->next) 32923147Sbloom ; 33023147Sbloom p->next = p1; 33123147Sbloom } else 33223147Sbloom itab = p1; 33323147Sbloom 33423147Sbloom p = p1; 33523147Sbloom SCPYN(p->line, t->ty_name); 33623147Sbloom p->xflag |= FOUND|CHANGE; 33723147Sbloom SCPYN(p->comn, t->ty_getty); 33823147Sbloom if (strcmp(t->ty_window, "") != 0) { 33923147Sbloom p->xflag |= WCHANGE; 34023147Sbloom SCPYN(p->wcmd, t->ty_window); 34123147Sbloom } 34213021Ssam contin1: 34313021Ssam ; 34413021Ssam } 34516452Sroot endttyent(); 34623147Sbloom p1 = (struct tab *)0; 34713021Ssam for (ALL) { 34813021Ssam if ((p->xflag&FOUND) == 0) { 34913021Ssam term(p); 35018542Sralph wterm(p); 35123147Sbloom if (p1) 35223147Sbloom p1->next = p->next; 35323147Sbloom else 35423147Sbloom itab = p->next; 35523147Sbloom free(p); 35623147Sbloom p = p1 ? p1 : itab; 35723147Sbloom } else { 35823147Sbloom /* window system should be started first */ 35923147Sbloom if (p->xflag&WCHANGE) { 36023147Sbloom wterm(p); 36123147Sbloom wstart(p); 36223147Sbloom } 36323147Sbloom if (p->xflag&CHANGE) { 36423147Sbloom term(p); 36523147Sbloom dfork(p); 36623147Sbloom } 36713021Ssam } 36823147Sbloom p1 = p; 36913021Ssam } 37013021Ssam } 37113021Ssam 3721029Sbill term(p) 37313021Ssam register struct tab *p; 3741029Sbill { 3751029Sbill 37613021Ssam if (p->pid != 0) { 3771029Sbill rmut(p); 3781029Sbill kill(p->pid, SIGKILL); 3791029Sbill } 3801029Sbill p->pid = 0; 38118542Sralph /* send SIGHUP to get rid of connections */ 38218542Sralph if (p->wpid > 0) 38318542Sralph kill(p->wpid, SIGHUP); 3841029Sbill } 3851029Sbill 3866816Ssam #include <sys/ioctl.h> 3876816Ssam 3881029Sbill dfork(p) 38913021Ssam struct tab *p; 3901029Sbill { 3911029Sbill register pid; 3925971Sroot time_t t; 3935971Sroot int dowait = 0; 3941029Sbill 3955971Sroot time(&t); 3965971Sroot p->gettycnt++; 3975971Sroot if ((t - p->gettytime) >= 60) { 3985971Sroot p->gettytime = t; 3995971Sroot p->gettycnt = 1; 40018542Sralph } else if (p->gettycnt >= 5) { 40118542Sralph dowait = 1; 40218542Sralph p->gettytime = t; 40318542Sralph p->gettycnt = 1; 4045971Sroot } 4051029Sbill pid = fork(); 40613021Ssam if (pid == 0) { 4076816Ssam signal(SIGTERM, SIG_DFL); 4086816Ssam signal(SIGHUP, SIG_IGN); 40922181Skarels sigsetmask(0); /* since can be called from masked code */ 4105971Sroot if (dowait) { 41118542Sralph syslog(LOG_ERR, "'%s %s' failing, sleeping", p->comn, p->line); 41218542Sralph closelog(); 4135971Sroot sleep(30); 4145971Sroot } 41518542Sralph execit(p->comn, p->line); 4161029Sbill exit(0); 4171029Sbill } 4181029Sbill p->pid = pid; 4191029Sbill } 4201029Sbill 42113021Ssam /* 42213021Ssam * Remove utmp entry. 42313021Ssam */ 4241029Sbill rmut(p) 42513021Ssam register struct tab *p; 4261029Sbill { 4271029Sbill register f; 4283608Swnj int found = 0; 42923147Sbloom static unsigned utmpsize; 43023147Sbloom static struct utmp *utmp; 43123147Sbloom register struct utmp *u; 43223147Sbloom int nutmp; 43323147Sbloom struct stat statbf; 4341029Sbill 43523147Sbloom f = open(utmpf, O_RDWR); 43613021Ssam if (f >= 0) { 43723147Sbloom fstat(f, &statbf); 43823147Sbloom if (utmpsize < statbf.st_size) { 43923147Sbloom utmpsize = statbf.st_size + 10 * sizeof(struct utmp); 44023147Sbloom if (utmp) 44123147Sbloom utmp = (struct utmp *)realloc(utmp, utmpsize); 44223147Sbloom else 44323147Sbloom utmp = (struct utmp *)malloc(utmpsize); 44423147Sbloom if (!utmp) 44523147Sbloom syslog(LOG_ERR, "utmp malloc failed"); 4461029Sbill } 44723147Sbloom if (statbf.st_size && utmp) { 44823147Sbloom nutmp = read(f, utmp, statbf.st_size); 44923147Sbloom nutmp /= sizeof(struct utmp); 45023147Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 45123147Sbloom if (SCMPN(u->ut_line, p->line) || 45223147Sbloom u->ut_name[0]==0) 45323147Sbloom continue; 45423147Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 45523147Sbloom SCPYN(u->ut_name, ""); 45623147Sbloom SCPYN(u->ut_host, ""); 45723147Sbloom time(&u->ut_time); 45823147Sbloom write(f, (char *)u, sizeof(*u)); 45923147Sbloom found++; 46023147Sbloom } 46123147Sbloom } 4621029Sbill close(f); 4631029Sbill } 4643608Swnj if (found) { 46513021Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 4663608Swnj if (f >= 0) { 4673608Swnj SCPYN(wtmp.ut_line, p->line); 4683608Swnj SCPYN(wtmp.ut_name, ""); 46912682Ssam SCPYN(wtmp.ut_host, ""); 4703608Swnj time(&wtmp.ut_time); 4713608Swnj write(f, (char *)&wtmp, sizeof(wtmp)); 4723608Swnj close(f); 4733608Swnj } 47417582Ssam /* 47517582Ssam * After a proper login force reset 47617582Ssam * of error detection code in dfork. 47717582Ssam */ 47817582Ssam p->gettytime = 0; 47922181Skarels p->windtime = 0; 4801029Sbill } 4811029Sbill } 4821029Sbill 4831029Sbill reset() 4841029Sbill { 48513021Ssam 4861029Sbill longjmp(sjbuf, 1); 4871029Sbill } 4882821Swnj 48913021Ssam jmp_buf idlebuf; 49013021Ssam 49113021Ssam idlehup() 49213021Ssam { 49313021Ssam 49413021Ssam longjmp(idlebuf, 1); 49513021Ssam } 49613021Ssam 4972821Swnj idle() 4982821Swnj { 4992821Swnj register struct tab *p; 5002821Swnj register pid; 5012821Swnj 50213021Ssam signal(SIGHUP, idlehup); 50318542Sralph for (EVER) { 50413021Ssam if (setjmp(idlebuf)) 50513021Ssam return; 5062821Swnj pid = wait((int *) 0); 50713021Ssam if (pid == -1) { 50813021Ssam sigpause(0); 50913021Ssam continue; 5102821Swnj } 51118542Sralph for (ALL) { 51218542Sralph /* if window system dies, mark it for restart */ 51318542Sralph if (p->wpid == pid) 51418542Sralph p->wpid = -1; 51513021Ssam if (p->pid == pid) { 51613021Ssam rmut(p); 51713021Ssam p->pid = -1; 51813021Ssam } 51918542Sralph } 5202821Swnj } 5212821Swnj } 52218542Sralph 52318542Sralph wterm(p) 52418542Sralph register struct tab *p; 52518542Sralph { 52618542Sralph if (p->wpid != 0) { 52718542Sralph kill(p->wpid, SIGKILL); 52818542Sralph } 52918542Sralph p->wpid = 0; 53018542Sralph } 53118542Sralph 53218542Sralph wstart(p) 53318542Sralph register struct tab *p; 53418542Sralph { 53522181Skarels register pid; 53622181Skarels time_t t; 53722181Skarels int dowait = 0; 53818542Sralph 53922181Skarels time(&t); 54022181Skarels p->windcnt++; 54122181Skarels if ((t - p->windtime) >= 60) { 54222181Skarels p->windtime = t; 54322181Skarels p->windcnt = 1; 54422181Skarels } else if (p->windcnt >= 5) { 54522181Skarels dowait = 1; 54622181Skarels p->windtime = t; 54722181Skarels p->windcnt = 1; 54822181Skarels } 54922181Skarels 55022181Skarels pid = fork(); 55122181Skarels 55222181Skarels if (pid == 0) { 55318542Sralph signal(SIGTERM, SIG_DFL); 55422181Skarels signal(SIGHUP, SIG_IGN); 55522181Skarels sigsetmask(0); /* since can be called from masked code */ 55622181Skarels if (dowait) { 55722181Skarels syslog(LOG_ERR, "'%s %s' failing, sleeping", p->wcmd, p->line); 55822181Skarels closelog(); 55922181Skarels sleep(30); 56022181Skarels } 56118542Sralph execit(p->wcmd, p->line); 56218542Sralph exit(0); 56318542Sralph } 56422181Skarels p->wpid = pid; 56518542Sralph } 56618542Sralph 56722181Skarels #define NARGS 20 /* must be at least 4 */ 56818542Sralph #define ARGLEN 512 /* total size for all the argument strings */ 56918542Sralph 57018542Sralph execit(s, arg) 57118542Sralph char *s; 57218542Sralph char *arg; /* last argument on line */ 57318542Sralph { 57418542Sralph char *argv[NARGS], args[ARGLEN], *envp[1]; 57518542Sralph register char *sp = s; 57618542Sralph register char *ap = args; 57718542Sralph register char c; 57818542Sralph register int i; 57918542Sralph 58018542Sralph /* 58118542Sralph * First we have to set up the argument vector. 58218542Sralph * "prog arg1 arg2" maps to exec("prog", "-", "arg1", "arg2"). 58318542Sralph */ 58418542Sralph for (i = 1; i < NARGS - 2; i++) { 58518542Sralph argv[i] = ap; 58618542Sralph for (EVER) { 58718542Sralph if ((c = *sp++) == '\0' || ap >= &args[ARGLEN-1]) { 58818542Sralph *ap = '\0'; 58918542Sralph goto done; 59018542Sralph } 59118542Sralph if (c == ' ') { 59218542Sralph *ap++ = '\0'; 59318542Sralph while (*sp == ' ') 59418542Sralph sp++; 59518542Sralph if (*sp == '\0') 59618542Sralph goto done; 59718542Sralph break; 59818542Sralph } 59918542Sralph *ap++ = c; 60018542Sralph } 60118542Sralph } 60218542Sralph done: 60318542Sralph argv[0] = argv[1]; 60418542Sralph argv[1] = "-"; 60518542Sralph argv[i+1] = arg; 60618542Sralph argv[i+2] = 0; 60718542Sralph envp[0] = 0; 60818542Sralph execve(argv[0], &argv[1], envp); 60918542Sralph /* report failure of exec */ 61018542Sralph syslog(LOG_ERR, "%s: %m", argv[0]); 61118542Sralph closelog(); 61218542Sralph sleep(10); /* prevent failures from eating machine */ 61318542Sralph } 614