1*1424Sroot static char *sccsid = "@(#)optr.c 1.1 (Berkeley) 10/13/80"; 2*1424Sroot #include "dump.h" 3*1424Sroot 4*1424Sroot /* 5*1424Sroot * This is from /usr/include/grp.h 6*1424Sroot * That defined struct group, which conflicts 7*1424Sroot * with the struct group defined in param.h 8*1424Sroot */ 9*1424Sroot struct Group { /* see getgrent(3) */ 10*1424Sroot char *gr_name; 11*1424Sroot char *gr_passwd; 12*1424Sroot int gr_gid; 13*1424Sroot char **gr_mem; 14*1424Sroot }; 15*1424Sroot struct Group *getgrnam(); 16*1424Sroot /* 17*1424Sroot * Query the operator; This fascist piece of code requires 18*1424Sroot * an exact response. 19*1424Sroot * It is intended to protect dump aborting by inquisitive 20*1424Sroot * people banging on the console terminal to see what is 21*1424Sroot * happening which might cause dump to croak, destroying 22*1424Sroot * a large number of hours of work. 23*1424Sroot * 24*1424Sroot * Every 2 minutes we reprint the message, alerting others 25*1424Sroot * that dump needs attention. 26*1424Sroot */ 27*1424Sroot int timeout; 28*1424Sroot char *attnmessage; /* attemtion message */ 29*1424Sroot query(question) 30*1424Sroot char *question; 31*1424Sroot { 32*1424Sroot char replybuffer[64]; 33*1424Sroot int back; 34*1424Sroot FILE *mytty; 35*1424Sroot 36*1424Sroot if ( (mytty = fopen("/dev/tty", "r")) == NULL){ 37*1424Sroot msg("fopen on /dev/tty fails\n"); 38*1424Sroot abort(); 39*1424Sroot } 40*1424Sroot attnmessage = question; 41*1424Sroot timeout = 0; 42*1424Sroot alarmcatch(); 43*1424Sroot for(;;){ 44*1424Sroot if ( fgets(replybuffer, 63, mytty) == NULL){ 45*1424Sroot if (ferror(mytty)){ 46*1424Sroot clearerr(mytty); 47*1424Sroot continue; 48*1424Sroot } 49*1424Sroot } else if ( (strcmp(replybuffer, "yes\n") == 0) || 50*1424Sroot (strcmp(replybuffer, "Yes\n") == 0)){ 51*1424Sroot back = 1; 52*1424Sroot goto done; 53*1424Sroot } else if ( (strcmp(replybuffer, "no\n") == 0) || 54*1424Sroot (strcmp(replybuffer, "No\n") == 0)){ 55*1424Sroot back = 0; 56*1424Sroot goto done; 57*1424Sroot } else { 58*1424Sroot msg("\"Yes\" or \"No\" ONLY!\n"); 59*1424Sroot alarmcatch(); 60*1424Sroot } 61*1424Sroot } 62*1424Sroot done: 63*1424Sroot /* 64*1424Sroot * Turn off the alarm, and reset the signal to trap out.. 65*1424Sroot */ 66*1424Sroot alarm(0); 67*1424Sroot if (signal(SIGALRM, sigalrm) == SIG_IGN) 68*1424Sroot signal(SIGALRM, SIG_IGN); 69*1424Sroot fclose(mytty); 70*1424Sroot return(back); 71*1424Sroot } 72*1424Sroot /* 73*1424Sroot * Alert the console operator, and enable the alarm clock to 74*1424Sroot * sleep for 2 minutes in case nobody comes to satisfy dump 75*1424Sroot */ 76*1424Sroot alarmcatch() 77*1424Sroot { 78*1424Sroot if (timeout) 79*1424Sroot msgtail("\n"); 80*1424Sroot msg("NEEDS ATTENTION: %s: (\"yes\" or \"no\") ", 81*1424Sroot attnmessage); 82*1424Sroot signal(SIGALRM, alarmcatch); 83*1424Sroot alarm(120); 84*1424Sroot timeout = 1; 85*1424Sroot } 86*1424Sroot /* 87*1424Sroot * Here if an inquisitive operator interrupts the dump program 88*1424Sroot */ 89*1424Sroot interrupt() 90*1424Sroot { 91*1424Sroot msg("Interrupt received. Do >>>YOU<<< know what are you doing?\n"); 92*1424Sroot if (query("Do you really want to abort dump?")) 93*1424Sroot dumpabort(); 94*1424Sroot signal(SIGINT, interrupt); 95*1424Sroot } 96*1424Sroot 97*1424Sroot /* 98*1424Sroot * The following variables and routines manage alerting 99*1424Sroot * operators to the status of dump. 100*1424Sroot * This works much like wall(1) does. 101*1424Sroot */ 102*1424Sroot struct Group *gp; 103*1424Sroot 104*1424Sroot /* 105*1424Sroot * Get the names from the group entry "operator" to notify. 106*1424Sroot */ 107*1424Sroot set_operators() 108*1424Sroot { 109*1424Sroot if (!notify) /*not going to notify*/ 110*1424Sroot return; 111*1424Sroot gp = getgrnam(OPGRENT); 112*1424Sroot endgrent(); 113*1424Sroot if (gp == (struct Group *)0){ 114*1424Sroot msg("No entry in /etc/group for %s.\n", 115*1424Sroot OPGRENT); 116*1424Sroot notify = 0; 117*1424Sroot return; 118*1424Sroot } 119*1424Sroot } 120*1424Sroot 121*1424Sroot struct tm *localtime(); 122*1424Sroot struct tm *localclock; 123*1424Sroot 124*1424Sroot /* 125*1424Sroot * We fork a child to do the actual broadcasting, so 126*1424Sroot * that the process control groups are not messed up 127*1424Sroot */ 128*1424Sroot broadcast(message) 129*1424Sroot char *message; 130*1424Sroot { 131*1424Sroot time_t clock; 132*1424Sroot FILE *f_utmp; 133*1424Sroot struct utmp utmp; 134*1424Sroot int nusers; 135*1424Sroot char **np; 136*1424Sroot int pid, s; 137*1424Sroot 138*1424Sroot switch (pid = fork()) { 139*1424Sroot case -1: 140*1424Sroot return; 141*1424Sroot case 0: 142*1424Sroot break; 143*1424Sroot default: 144*1424Sroot while (wait(&s) != pid) 145*1424Sroot continue; 146*1424Sroot return; 147*1424Sroot } 148*1424Sroot 149*1424Sroot if (!notify || gp == 0) 150*1424Sroot return; 151*1424Sroot clock = time(0); 152*1424Sroot localclock = localtime(&clock); 153*1424Sroot 154*1424Sroot if((f_utmp = fopen("/etc/utmp", "r")) == NULL) { 155*1424Sroot msg("Cannot open /etc/utmp\n"); 156*1424Sroot return; 157*1424Sroot } 158*1424Sroot 159*1424Sroot nusers = 0; 160*1424Sroot while (!feof(f_utmp)){ 161*1424Sroot if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1) 162*1424Sroot break; 163*1424Sroot if (utmp.ut_name[0] == 0) 164*1424Sroot continue; 165*1424Sroot nusers++; 166*1424Sroot for (np = gp->gr_mem; *np; np++){ 167*1424Sroot if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 168*1424Sroot continue; 169*1424Sroot /* 170*1424Sroot * Do not send messages to operators on dialups 171*1424Sroot */ 172*1424Sroot if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 173*1424Sroot continue; 174*1424Sroot #ifdef DEBUG 175*1424Sroot msg("Message to %s at %s\n", 176*1424Sroot utmp.ut_name, utmp.ut_line); 177*1424Sroot #endif DEBUG 178*1424Sroot sendmes(utmp.ut_line, message); 179*1424Sroot } 180*1424Sroot } 181*1424Sroot fclose(f_utmp); 182*1424Sroot Exit(0); /* the wait in this same routine will catch this */ 183*1424Sroot /* NOTREACHED */ 184*1424Sroot } 185*1424Sroot 186*1424Sroot sendmes(tty, message) 187*1424Sroot char *tty, *message; 188*1424Sroot { 189*1424Sroot char t[50], buf[BUFSIZ]; 190*1424Sroot register char *cp; 191*1424Sroot register int c, ch; 192*1424Sroot int msize; 193*1424Sroot FILE *f_tty; 194*1424Sroot 195*1424Sroot msize = strlen(message); 196*1424Sroot strcpy(t, "/dev/"); 197*1424Sroot strcat(t, tty); 198*1424Sroot 199*1424Sroot if((f_tty = fopen(t, "w")) != NULL) { 200*1424Sroot setbuf(f_tty, buf); 201*1424Sroot fprintf(f_tty, "\nMessage from the dump program to all operators at %d:%02d ...\r\n\n" 202*1424Sroot ,localclock->tm_hour 203*1424Sroot ,localclock->tm_min); 204*1424Sroot for (cp = message, c = msize; c-- > 0; cp++) { 205*1424Sroot ch = *cp; 206*1424Sroot if (ch == '\n') 207*1424Sroot putc('\r', f_tty); 208*1424Sroot putc(ch, f_tty); 209*1424Sroot } 210*1424Sroot fclose(f_tty); 211*1424Sroot } 212*1424Sroot } 213*1424Sroot 214*1424Sroot /* 215*1424Sroot * print out an estimate of the amount of time left to do the dump 216*1424Sroot */ 217*1424Sroot 218*1424Sroot time_t tschedule = 0; 219*1424Sroot 220*1424Sroot timeest() 221*1424Sroot { 222*1424Sroot time_t tnow, deltat; 223*1424Sroot 224*1424Sroot time (&tnow); 225*1424Sroot if (tnow >= tschedule){ 226*1424Sroot tschedule = tnow + 300; 227*1424Sroot if (blockswritten < 500) 228*1424Sroot return; 229*1424Sroot deltat = tstart_writing - tnow + 230*1424Sroot (((1.0*(tnow - tstart_writing))/blockswritten) * esize); 231*1424Sroot msg("%3.2f%% done, finished in %d:%02d\n", 232*1424Sroot (blockswritten*100.0)/esize, 233*1424Sroot deltat/3600, (deltat%3600)/60); 234*1424Sroot } 235*1424Sroot } 236*1424Sroot 237*1424Sroot int blocksontape() 238*1424Sroot { 239*1424Sroot /* 240*1424Sroot * esize: total number of blocks estimated over all reels 241*1424Sroot * blockswritten: blocks actually written, over all reels 242*1424Sroot * etapes: estimated number of tapes to write 243*1424Sroot * 244*1424Sroot * tsize: blocks can write on this reel 245*1424Sroot * asize: blocks written on this reel 246*1424Sroot * tapeno: number of tapes written so far 247*1424Sroot */ 248*1424Sroot if (tapeno == etapes) 249*1424Sroot return(esize - (etapes - 1)*tsize); 250*1424Sroot return(tsize); 251*1424Sroot } 252*1424Sroot 253*1424Sroot /* VARARGS1 */ 254*1424Sroot /* ARGSUSED */ 255*1424Sroot msg(fmt, a1, a2, a3, a4, a5) 256*1424Sroot char *fmt; 257*1424Sroot { 258*1424Sroot fprintf(stderr," DUMP: "); 259*1424Sroot #ifdef TDEBUG 260*1424Sroot fprintf(stderr,"pid=%d ", getpid()); 261*1424Sroot #endif 262*1424Sroot fprintf(stderr, fmt, a1, a2, a3, a4, a5); 263*1424Sroot fflush(stdout); 264*1424Sroot fflush(stderr); 265*1424Sroot } 266*1424Sroot 267*1424Sroot /* VARARGS1 */ 268*1424Sroot /* ARGSUSED */ 269*1424Sroot msgtail(fmt, a1, a2, a3, a4, a5) 270*1424Sroot char *fmt; 271*1424Sroot { 272*1424Sroot fprintf(stderr, fmt, a1, a2, a3, a4, a5); 273*1424Sroot } 274*1424Sroot /* 275*1424Sroot * Tell the operator what has to be done; 276*1424Sroot * we don't actually do it 277*1424Sroot */ 278*1424Sroot 279*1424Sroot getfstab() 280*1424Sroot { 281*1424Sroot register struct fstab *dt; 282*1424Sroot FILE *fstabfile; 283*1424Sroot 284*1424Sroot nfstab = 0; 285*1424Sroot if ( (fstabfile = fopen(FSTAB, "r")) == NULL){ 286*1424Sroot msg("Can't open %s for dump table information.\n", FSTAB); 287*1424Sroot } else { 288*1424Sroot for (nfstab = 0, dt = fstab; nfstab < MAXFSTAB;){ 289*1424Sroot if (feof(fstabfile)) 290*1424Sroot break; 291*1424Sroot fscanf(fstabfile, FSTABFMT, FSTABARG(dt)); 292*1424Sroot if (!strcmp(dt->fs_type, "rw") || 293*1424Sroot !strcmp(dt->fs_type, "ro")) 294*1424Sroot nfstab++, dt++; 295*1424Sroot } 296*1424Sroot fclose(fstabfile); 297*1424Sroot } 298*1424Sroot } 299*1424Sroot 300*1424Sroot /* 301*1424Sroot * Search in the fstab for a file name. 302*1424Sroot * This file name can be either the special or the path file name. 303*1424Sroot * 304*1424Sroot * The entries in the fstab are the BLOCK special names, not the 305*1424Sroot * character special names. 306*1424Sroot * The caller of fstabsearch assures that the character device 307*1424Sroot * is dumped (that is much faster) 308*1424Sroot * 309*1424Sroot * The file name can omit the leading '/'. 310*1424Sroot */ 311*1424Sroot struct fstab *fstabsearch(key) 312*1424Sroot char *key; 313*1424Sroot { 314*1424Sroot register struct fstab *dt; 315*1424Sroot int i; 316*1424Sroot int keylength; 317*1424Sroot char *rawname(); 318*1424Sroot 319*1424Sroot keylength = min(strlen(key), sizeof (dt->fs_file)); 320*1424Sroot for (i = 0, dt = fstab; i < nfstab; i++, dt++){ 321*1424Sroot if (strncmp(dt->fs_file, key, keylength) == 0) 322*1424Sroot return(dt); 323*1424Sroot if (strncmp(dt->fs_spec, key, keylength) == 0) 324*1424Sroot return(dt); 325*1424Sroot if (strncmp(rawname(dt->fs_spec), key, keylength) == 0) 326*1424Sroot return(dt); 327*1424Sroot 328*1424Sroot if (key[0] != '/'){ 329*1424Sroot if ( (dt->fs_spec[0] == '/') 330*1424Sroot && (strncmp(dt->fs_spec+1, key, keylength) == 0)) 331*1424Sroot return(dt); 332*1424Sroot if ( (dt->fs_file[0] == '/') 333*1424Sroot && (strncmp(dt->fs_file+1, key, keylength) == 0)) 334*1424Sroot return(dt); 335*1424Sroot } 336*1424Sroot } 337*1424Sroot return(0); 338*1424Sroot } 339*1424Sroot 340*1424Sroot /* 341*1424Sroot * Tell the operator what to do 342*1424Sroot */ 343*1424Sroot lastdump() 344*1424Sroot { 345*1424Sroot char *lastname; 346*1424Sroot char *date; 347*1424Sroot register int i; 348*1424Sroot time_t tnow; 349*1424Sroot register struct fstab *dt; 350*1424Sroot int dumpme; 351*1424Sroot register struct idates *itwalk; 352*1424Sroot 353*1424Sroot int idatesort(); 354*1424Sroot 355*1424Sroot time(&tnow); 356*1424Sroot getfstab(); /* /etc/fstab input */ 357*1424Sroot inititimes(); /* /etc/dumpdates input */ 358*1424Sroot qsort(idatev, nidates, sizeof(struct idates *), idatesort); 359*1424Sroot 360*1424Sroot fprintf(stdout, "Last dump(s) done (Dump '*' file systems):\n"); 361*1424Sroot lastname = "??"; 362*1424Sroot ITITERATE(i, itwalk){ 363*1424Sroot if (strncmp(lastname, itwalk->id_name, sizeof(itwalk->id_name)) == 0) 364*1424Sroot continue; 365*1424Sroot date = (char *)ctime(&itwalk->id_ddate); 366*1424Sroot date[16] = '\0'; /* blast away seconds and year */ 367*1424Sroot lastname = itwalk->id_name; 368*1424Sroot dt = fstabsearch(itwalk->id_name); 369*1424Sroot dumpme = ( (dt != 0) 370*1424Sroot && (dt->fs_freq != 0) 371*1424Sroot && (itwalk->id_ddate < tnow - (dt->fs_freq*DAY))); 372*1424Sroot fprintf(stdout,"%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 373*1424Sroot dumpme ? '*' : ' ', 374*1424Sroot itwalk->id_name, 375*1424Sroot dt ? dt->fs_file : 0, 376*1424Sroot itwalk->id_incno, 377*1424Sroot date 378*1424Sroot ); 379*1424Sroot } 380*1424Sroot } 381*1424Sroot 382*1424Sroot int idatesort(p1, p2) 383*1424Sroot struct idates **p1, **p2; 384*1424Sroot { 385*1424Sroot int diff; 386*1424Sroot 387*1424Sroot diff = strncmp((*p1)->id_name, (*p2)->id_name, sizeof((*p1)->id_name)); 388*1424Sroot if (diff == 0) 389*1424Sroot return ((*p2)->id_ddate - (*p1)->id_ddate); 390*1424Sroot else 391*1424Sroot return (diff); 392*1424Sroot } 393*1424Sroot 394*1424Sroot int max(a,b) 395*1424Sroot { 396*1424Sroot return(a>b?a:b); 397*1424Sroot } 398*1424Sroot int min(a,b) 399*1424Sroot { 400*1424Sroot return(a<b?a:b); 401*1424Sroot } 402