optr.c revision 1558
1131702Smbr/*- 2131702Smbr * Copyright (c) 1980, 1988, 1993 3131702Smbr * The Regents of the University of California. All rights reserved. 4131702Smbr * 5131702Smbr * Redistribution and use in source and binary forms, with or without 6131702Smbr * modification, are permitted provided that the following conditions 7131702Smbr * are met: 8131702Smbr * 1. Redistributions of source code must retain the above copyright 9131702Smbr * notice, this list of conditions and the following disclaimer. 10131702Smbr * 2. Redistributions in binary form must reproduce the above copyright 11131702Smbr * notice, this list of conditions and the following disclaimer in the 12131702Smbr * documentation and/or other materials provided with the distribution. 13131702Smbr * 3. All advertising materials mentioning features or use of this software 14131702Smbr * must display the following acknowledgement: 15131702Smbr * This product includes software developed by the University of 16131702Smbr * California, Berkeley and its contributors. 17131702Smbr * 4. Neither the name of the University nor the names of its contributors 18131702Smbr * may be used to endorse or promote products derived from this software 19131702Smbr * without specific prior written permission. 20131702Smbr * 21131702Smbr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22131702Smbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23131702Smbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24131702Smbr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25131702Smbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26131702Smbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27131702Smbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28131702Smbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29131702Smbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30131702Smbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31131702Smbr * SUCH DAMAGE. 32131702Smbr */ 33131702Smbr 34131702Smbr#ifndef lint 35131702Smbrstatic char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; 36131702Smbr#endif /* not lint */ 37131702Smbr 38131702Smbr#include <sys/param.h> 39131702Smbr#include <sys/wait.h> 40131702Smbr#include <sys/time.h> 41131702Smbr 42131702Smbr#include <errno.h> 43131702Smbr#include <fstab.h> 44131702Smbr#include <grp.h> 45131702Smbr#include <signal.h> 46131702Smbr#include <stdio.h> 47131702Smbr#ifdef __STDC__ 48131702Smbr#include <stdlib.h> 49131702Smbr#include <string.h> 50131702Smbr#include <stdarg.h> 51131702Smbr#endif 52131702Smbr#include <tzfile.h> 53131702Smbr#ifdef __STDC__ 54131702Smbr#include <unistd.h> 55131702Smbr#endif 56131702Smbr#include <utmp.h> 57131702Smbr#ifndef __STDC__ 58131702Smbr#include <varargs.h> 59131702Smbr#endif 60131702Smbr 61#include "dump.h" 62#include "pathnames.h" 63 64void alarmcatch __P((/* int, int */)); 65int datesort __P((const void *, const void *)); 66static void sendmes __P((char *, char *)); 67 68/* 69 * Query the operator; This previously-fascist piece of code 70 * no longer requires an exact response. 71 * It is intended to protect dump aborting by inquisitive 72 * people banging on the console terminal to see what is 73 * happening which might cause dump to croak, destroying 74 * a large number of hours of work. 75 * 76 * Every 2 minutes we reprint the message, alerting others 77 * that dump needs attention. 78 */ 79static int timeout; 80static char *attnmessage; /* attention message */ 81 82int 83query(question) 84 char *question; 85{ 86 char replybuffer[64]; 87 int back, errcount; 88 FILE *mytty; 89 90 if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 91 quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 92 attnmessage = question; 93 timeout = 0; 94 alarmcatch(); 95 back = -1; 96 errcount = 0; 97 do { 98 if (fgets(replybuffer, 63, mytty) == NULL) { 99 clearerr(mytty); 100 if (++errcount > 30) /* XXX ugly */ 101 quit("excessive operator query failures\n"); 102 } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 103 back = 1; 104 } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 105 back = 0; 106 } else { 107 (void) fprintf(stderr, 108 " DUMP: \"Yes\" or \"No\"?\n"); 109 (void) fprintf(stderr, 110 " DUMP: %s: (\"yes\" or \"no\") ", question); 111 } 112 } while (back < 0); 113 114 /* 115 * Turn off the alarm, and reset the signal to trap out.. 116 */ 117 (void) alarm(0); 118 if (signal(SIGALRM, sig) == SIG_IGN) 119 signal(SIGALRM, SIG_IGN); 120 (void) fclose(mytty); 121 return(back); 122} 123 124char lastmsg[100]; 125 126/* 127 * Alert the console operator, and enable the alarm clock to 128 * sleep for 2 minutes in case nobody comes to satisfy dump 129 */ 130void 131alarmcatch() 132{ 133 if (notify == 0) { 134 if (timeout == 0) 135 (void) fprintf(stderr, 136 " DUMP: %s: (\"yes\" or \"no\") ", 137 attnmessage); 138 else 139 msgtail("\7\7"); 140 } else { 141 if (timeout) { 142 msgtail("\n"); 143 broadcast(""); /* just print last msg */ 144 } 145 (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 146 attnmessage); 147 } 148 signal(SIGALRM, alarmcatch); 149 (void) alarm(120); 150 timeout = 1; 151} 152 153/* 154 * Here if an inquisitive operator interrupts the dump program 155 */ 156void 157interrupt(signo) 158 int signo; 159{ 160 msg("Interrupt received.\n"); 161 if (query("Do you want to abort dump?")) 162 dumpabort(0); 163} 164 165/* 166 * The following variables and routines manage alerting 167 * operators to the status of dump. 168 * This works much like wall(1) does. 169 */ 170struct group *gp; 171 172/* 173 * Get the names from the group entry "operator" to notify. 174 */ 175void 176set_operators() 177{ 178 if (!notify) /*not going to notify*/ 179 return; 180 gp = getgrnam(OPGRENT); 181 (void) endgrent(); 182 if (gp == NULL) { 183 msg("No group entry for %s.\n", OPGRENT); 184 notify = 0; 185 return; 186 } 187} 188 189struct tm *localclock; 190 191/* 192 * We fork a child to do the actual broadcasting, so 193 * that the process control groups are not messed up 194 */ 195void 196broadcast(message) 197 char *message; 198{ 199 time_t clock; 200 FILE *f_utmp; 201 struct utmp utmp; 202 char **np; 203 int pid, s; 204 205 if (!notify || gp == NULL) 206 return; 207 208 switch (pid = fork()) { 209 case -1: 210 return; 211 case 0: 212 break; 213 default: 214 while (wait(&s) != pid) 215 continue; 216 return; 217 } 218 219 clock = time((time_t *)0); 220 localclock = localtime(&clock); 221 222 if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 223 msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 224 return; 225 } 226 227 while (!feof(f_utmp)) { 228 if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1) 229 break; 230 if (utmp.ut_name[0] == 0) 231 continue; 232 for (np = gp->gr_mem; *np; np++) { 233 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 234 continue; 235 /* 236 * Do not send messages to operators on dialups 237 */ 238 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 239 continue; 240#ifdef DEBUG 241 msg("Message to %s at %s\n", *np, utmp.ut_line); 242#endif 243 sendmes(utmp.ut_line, message); 244 } 245 } 246 (void) fclose(f_utmp); 247 Exit(0); /* the wait in this same routine will catch this */ 248 /* NOTREACHED */ 249} 250 251static void 252sendmes(tty, message) 253 char *tty, *message; 254{ 255 char t[50], buf[BUFSIZ]; 256 register char *cp; 257 int lmsg = 1; 258 FILE *f_tty; 259 260 (void) strcpy(t, _PATH_DEV); 261 (void) strcat(t, tty); 262 263 if ((f_tty = fopen(t, "w")) != NULL) { 264 setbuf(f_tty, buf); 265 (void) fprintf(f_tty, 266 "\n\ 267\7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 268DUMP: NEEDS ATTENTION: ", 269 localclock->tm_hour, localclock->tm_min); 270 for (cp = lastmsg; ; cp++) { 271 if (*cp == '\0') { 272 if (lmsg) { 273 cp = message; 274 if (*cp == '\0') 275 break; 276 lmsg = 0; 277 } else 278 break; 279 } 280 if (*cp == '\n') 281 (void) putc('\r', f_tty); 282 (void) putc(*cp, f_tty); 283 } 284 (void) fclose(f_tty); 285 } 286} 287 288/* 289 * print out an estimate of the amount of time left to do the dump 290 */ 291 292time_t tschedule = 0; 293 294void 295timeest() 296{ 297 time_t tnow, deltat; 298 299 (void) time((time_t *) &tnow); 300 if (tnow >= tschedule) { 301 tschedule = tnow + 300; 302 if (blockswritten < 500) 303 return; 304 deltat = tstart_writing - tnow + 305 (1.0 * (tnow - tstart_writing)) 306 / blockswritten * tapesize; 307 msg("%3.2f%% done, finished in %d:%02d\n", 308 (blockswritten * 100.0) / tapesize, 309 deltat / 3600, (deltat % 3600) / 60); 310 } 311} 312 313void 314#if __STDC__ 315msg(const char *fmt, ...) 316#else 317msg(fmt, va_alist) 318 char *fmt; 319 va_dcl 320#endif 321{ 322 va_list ap; 323 324 (void) fprintf(stderr," DUMP: "); 325#ifdef TDEBUG 326 (void) fprintf(stderr, "pid=%d ", getpid()); 327#endif 328#if __STDC__ 329 va_start(ap, fmt); 330#else 331 va_start(ap); 332#endif 333 (void) vfprintf(stderr, fmt, ap); 334 (void) fflush(stdout); 335 (void) fflush(stderr); 336 (void) vsprintf(lastmsg, fmt, ap); 337 va_end(ap); 338} 339 340void 341#if __STDC__ 342msgtail(const char *fmt, ...) 343#else 344msgtail(fmt, va_alist) 345 char *fmt; 346 va_dcl 347#endif 348{ 349 va_list ap; 350#if __STDC__ 351 va_start(ap, fmt); 352#else 353 va_start(ap); 354#endif 355 (void) vfprintf(stderr, fmt, ap); 356 va_end(ap); 357} 358 359void 360#if __STDC__ 361quit(const char *fmt, ...) 362#else 363quit(fmt, va_alist) 364 char *fmt; 365 va_dcl 366#endif 367{ 368 va_list ap; 369 370 (void) fprintf(stderr," DUMP: "); 371#ifdef TDEBUG 372 (void) fprintf(stderr, "pid=%d ", getpid()); 373#endif 374#if __STDC__ 375 va_start(ap, fmt); 376#else 377 va_start(ap); 378#endif 379 (void) vfprintf(stderr, fmt, ap); 380 va_end(ap); 381 (void) fflush(stdout); 382 (void) fflush(stderr); 383 dumpabort(0); 384} 385 386/* 387 * Tell the operator what has to be done; 388 * we don't actually do it 389 */ 390 391struct fstab * 392allocfsent(fs) 393 register struct fstab *fs; 394{ 395 register struct fstab *new; 396 397 new = (struct fstab *)malloc(sizeof (*fs)); 398 if (new == NULL || 399 (new->fs_file = strdup(fs->fs_file)) == NULL || 400 (new->fs_type = strdup(fs->fs_type)) == NULL || 401 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 402 quit("%s\n", strerror(errno)); 403 new->fs_passno = fs->fs_passno; 404 new->fs_freq = fs->fs_freq; 405 return (new); 406} 407 408struct pfstab { 409 struct pfstab *pf_next; 410 struct fstab *pf_fstab; 411}; 412 413static struct pfstab *table; 414 415void 416getfstab() 417{ 418 register struct fstab *fs; 419 register struct pfstab *pf; 420 421 if (setfsent() == 0) { 422 msg("Can't open %s for dump table information: %s\n", 423 _PATH_FSTAB, strerror(errno)); 424 return; 425 } 426 while ((fs = getfsent()) != NULL) { 427 if (strcmp(fs->fs_type, FSTAB_RW) && 428 strcmp(fs->fs_type, FSTAB_RO) && 429 strcmp(fs->fs_type, FSTAB_RQ)) 430 continue; 431 fs = allocfsent(fs); 432 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 433 quit("%s\n", strerror(errno)); 434 pf->pf_fstab = fs; 435 pf->pf_next = table; 436 table = pf; 437 } 438 (void) endfsent(); 439} 440 441/* 442 * Search in the fstab for a file name. 443 * This file name can be either the special or the path file name. 444 * 445 * The entries in the fstab are the BLOCK special names, not the 446 * character special names. 447 * The caller of fstabsearch assures that the character device 448 * is dumped (that is much faster) 449 * 450 * The file name can omit the leading '/'. 451 */ 452struct fstab * 453fstabsearch(key) 454 char *key; 455{ 456 register struct pfstab *pf; 457 register struct fstab *fs; 458 char *rn; 459 460 for (pf = table; pf != NULL; pf = pf->pf_next) { 461 fs = pf->pf_fstab; 462 if (strcmp(fs->fs_file, key) == 0 || 463 strcmp(fs->fs_spec, key) == 0) 464 return (fs); 465 rn = rawname(fs->fs_spec); 466 if (rn != NULL && strcmp(rn, key) == 0) 467 return (fs); 468 if (key[0] != '/') { 469 if (*fs->fs_spec == '/' && 470 strcmp(fs->fs_spec + 1, key) == 0) 471 return (fs); 472 if (*fs->fs_file == '/' && 473 strcmp(fs->fs_file + 1, key) == 0) 474 return (fs); 475 } 476 } 477 return (NULL); 478} 479 480/* 481 * Tell the operator what to do 482 */ 483void 484lastdump(arg) 485 char arg; /* w ==> just what to do; W ==> most recent dumps */ 486{ 487 register int i; 488 register struct fstab *dt; 489 register struct dumpdates *dtwalk; 490 char *lastname, *date; 491 int dumpme; 492 time_t tnow; 493 494 (void) time(&tnow); 495 getfstab(); /* /etc/fstab input */ 496 initdumptimes(); /* /etc/dumpdates input */ 497 qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); 498 499 if (arg == 'w') 500 (void) printf("Dump these file systems:\n"); 501 else 502 (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 503 lastname = "??"; 504 ITITERATE(i, dtwalk) { 505 if (strncmp(lastname, dtwalk->dd_name, 506 sizeof(dtwalk->dd_name)) == 0) 507 continue; 508 date = (char *)ctime(&dtwalk->dd_ddate); 509 date[16] = '\0'; /* blast away seconds and year */ 510 lastname = dtwalk->dd_name; 511 dt = fstabsearch(dtwalk->dd_name); 512 dumpme = (dt != NULL && 513 dt->fs_freq != 0 && 514 dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); 515 if (arg != 'w' || dumpme) 516 (void) printf( 517 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 518 dumpme && (arg != 'w') ? '>' : ' ', 519 dtwalk->dd_name, 520 dt ? dt->fs_file : "", 521 dtwalk->dd_level, 522 date); 523 } 524} 525 526int 527datesort(a1, a2) 528 const void *a1, *a2; 529{ 530 struct dumpdates *d1 = *(struct dumpdates **)a1; 531 struct dumpdates *d2 = *(struct dumpdates **)a2; 532 int diff; 533 534 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 535 if (diff == 0) 536 return (d2->dd_ddate - d1->dd_ddate); 537 return (diff); 538} 539