1 /*- 2 * Copyright (c) 1980, 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)optr.c 5.8 (Berkeley) 03/07/91"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/wait.h> 14 #include <ufs/dir.h> 15 #include <signal.h> 16 #include <time.h> 17 #include <fstab.h> 18 #include <grp.h> 19 #include <varargs.h> 20 #include <utmp.h> 21 #include <tzfile.h> 22 #include <errno.h> 23 #include <stdio.h> 24 #ifdef __STDC__ 25 #include <unistd.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #endif 29 #include "dump.h" 30 #include "pathnames.h" 31 32 static void alarmcatch(); 33 static void sendmes(); 34 35 /* 36 * Query the operator; This previously-fascist piece of code 37 * no longer requires an exact response. 38 * It is intended to protect dump aborting by inquisitive 39 * people banging on the console terminal to see what is 40 * happening which might cause dump to croak, destroying 41 * a large number of hours of work. 42 * 43 * Every 2 minutes we reprint the message, alerting others 44 * that dump needs attention. 45 */ 46 int timeout; 47 char *attnmessage; /* attention message */ 48 49 int 50 query(question) 51 char *question; 52 { 53 char replybuffer[64]; 54 int back, errcount; 55 FILE *mytty; 56 57 if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 58 quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 59 attnmessage = question; 60 timeout = 0; 61 alarmcatch(); 62 back = -1; 63 errcount = 0; 64 do { 65 if (fgets(replybuffer, 63, mytty) == NULL) { 66 clearerr(mytty); 67 if (++errcount > 30) /* XXX ugly */ 68 quit("excessive operator query failures\n"); 69 } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 70 back = 1; 71 } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 72 back = 0; 73 } else { 74 (void) fprintf(stderr, 75 " DUMP: \"Yes\" or \"No\"?\n"); 76 (void) fprintf(stderr, 77 " DUMP: %s: (\"yes\" or \"no\") ", question); 78 } 79 } while (back < 0); 80 81 /* 82 * Turn off the alarm, and reset the signal to trap out.. 83 */ 84 alarm(0); 85 if (signal(SIGALRM, sigalrm) == SIG_IGN) 86 signal(SIGALRM, SIG_IGN); 87 fclose(mytty); 88 return(back); 89 } 90 91 char lastmsg[100]; 92 93 /* 94 * Alert the console operator, and enable the alarm clock to 95 * sleep for 2 minutes in case nobody comes to satisfy dump 96 */ 97 static void 98 alarmcatch() 99 { 100 if (notify == 0) { 101 if (timeout == 0) 102 (void) fprintf(stderr, 103 " DUMP: %s: (\"yes\" or \"no\") ", 104 attnmessage); 105 else 106 msgtail("\7\7"); 107 } else { 108 if (timeout) { 109 msgtail("\n"); 110 broadcast(""); /* just print last msg */ 111 } 112 (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 113 attnmessage); 114 } 115 signal(SIGALRM, alarmcatch); 116 alarm(120); 117 timeout = 1; 118 } 119 120 /* 121 * Here if an inquisitive operator interrupts the dump program 122 */ 123 void 124 interrupt() 125 { 126 msg("Interrupt received.\n"); 127 if (query("Do you want to abort dump?")) 128 dumpabort(); 129 } 130 131 /* 132 * The following variables and routines manage alerting 133 * operators to the status of dump. 134 * This works much like wall(1) does. 135 */ 136 struct group *gp; 137 138 /* 139 * Get the names from the group entry "operator" to notify. 140 */ 141 void 142 set_operators() 143 { 144 if (!notify) /*not going to notify*/ 145 return; 146 gp = getgrnam(OPGRENT); 147 endgrent(); 148 if (gp == NULL) { 149 msg("No group entry for %s.\n", OPGRENT); 150 notify = 0; 151 return; 152 } 153 } 154 155 struct tm *localtime(); 156 struct tm *localclock; 157 158 /* 159 * We fork a child to do the actual broadcasting, so 160 * that the process control groups are not messed up 161 */ 162 void 163 broadcast(message) 164 char *message; 165 { 166 time_t clock; 167 FILE *f_utmp; 168 struct utmp utmp; 169 int nusers; 170 char **np; 171 int pid, s; 172 173 if (!notify || gp == NULL) 174 return; 175 176 switch (pid = fork()) { 177 case -1: 178 return; 179 case 0: 180 break; 181 default: 182 while (wait(&s) != pid) 183 continue; 184 return; 185 } 186 187 clock = time(0); 188 localclock = localtime(&clock); 189 190 if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 191 msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 192 return; 193 } 194 195 nusers = 0; 196 while (!feof(f_utmp)) { 197 if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1) 198 break; 199 if (utmp.ut_name[0] == 0) 200 continue; 201 nusers++; 202 for (np = gp->gr_mem; *np; np++) { 203 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 204 continue; 205 /* 206 * Do not send messages to operators on dialups 207 */ 208 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 209 continue; 210 #ifdef DEBUG 211 msg("Message to %s at %s\n", *np, utmp.ut_line); 212 #endif 213 sendmes(utmp.ut_line, message); 214 } 215 } 216 (void) fclose(f_utmp); 217 Exit(0); /* the wait in this same routine will catch this */ 218 /* NOTREACHED */ 219 } 220 221 static void 222 sendmes(tty, message) 223 char *tty, *message; 224 { 225 char t[50], buf[BUFSIZ]; 226 register char *cp; 227 int lmsg = 1; 228 FILE *f_tty; 229 230 (void) strcpy(t, _PATH_DEV); 231 (void) strcat(t, tty); 232 233 if ((f_tty = fopen(t, "w")) != NULL) { 234 setbuf(f_tty, buf); 235 (void) fprintf(f_tty, 236 "\n\ 237 \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 238 DUMP: NEEDS ATTENTION: ", 239 localclock->tm_hour, localclock->tm_min); 240 for (cp = lastmsg; ; cp++) { 241 if (*cp == '\0') { 242 if (lmsg) { 243 cp = message; 244 if (*cp == '\0') 245 break; 246 lmsg = 0; 247 } else 248 break; 249 } 250 if (*cp == '\n') 251 (void) putc('\r', f_tty); 252 (void) putc(*cp, f_tty); 253 } 254 (void) fclose(f_tty); 255 } 256 } 257 258 /* 259 * print out an estimate of the amount of time left to do the dump 260 */ 261 262 time_t tschedule = 0; 263 264 void 265 timeest() 266 { 267 time_t tnow, deltat; 268 269 time (&tnow); 270 if (tnow >= tschedule) { 271 tschedule = tnow + 300; 272 if (blockswritten < 500) 273 return; 274 deltat = tstart_writing - tnow + 275 (1.0 * (tnow - tstart_writing)) 276 / blockswritten * tapesize; 277 msg("%3.2f%% done, finished in %d:%02d\n", 278 (blockswritten * 100.0) / tapesize, 279 deltat / 3600, (deltat % 3600) / 60); 280 } 281 } 282 283 /* 284 * tapesize: total number of blocks estimated over all reels 285 * blockswritten: blocks actually written, over all reels 286 * etapes: estimated number of tapes to write 287 * 288 * tsize: blocks can write on this reel 289 * asize: blocks written on this reel 290 * tapeno: number of tapes written so far 291 */ 292 int 293 blocksontape() 294 { 295 if (tapeno == etapes) 296 return (tapesize - (etapes - 1) * tsize); 297 return (tsize); 298 } 299 300 #ifdef lint 301 302 /* VARARGS1 */ 303 void msg(fmt) char *fmt; { strcpy(lastmsg, fmt); } 304 305 /* VARARGS1 */ 306 void msgtail(fmt) char *fmt; { fmt = fmt; } 307 308 void quit(fmt) char *fmt; { msg(fmt); dumpabort(); } 309 310 #else /* lint */ 311 312 void 313 msg(va_alist) 314 va_dcl 315 { 316 va_list ap; 317 char *fmt; 318 319 (void) fprintf(stderr," DUMP: "); 320 #ifdef TDEBUG 321 (void) fprintf(stderr, "pid=%d ", getpid()); 322 #endif 323 va_start(ap); 324 fmt = va_arg(ap, char *); 325 (void) vfprintf(stderr, fmt, ap); 326 va_end(ap); 327 (void) fflush(stdout); 328 (void) fflush(stderr); 329 va_start(ap); 330 fmt = va_arg(ap, char *); 331 (void) vsprintf(lastmsg, fmt, ap); 332 va_end(ap); 333 } 334 335 void 336 msgtail(va_alist) 337 va_dcl 338 { 339 va_list ap; 340 char *fmt; 341 342 va_start(ap); 343 fmt = va_arg(ap, char *); 344 (void) vfprintf(stderr, fmt, ap); 345 va_end(ap); 346 } 347 348 void 349 quit(va_alist) 350 va_dcl 351 { 352 va_list ap; 353 char *fmt; 354 355 (void) fprintf(stderr," DUMP: "); 356 #ifdef TDEBUG 357 (void) fprintf(stderr, "pid=%d ", getpid()); 358 #endif 359 va_start(ap); 360 fmt = va_arg(ap, char *); 361 vfprintf(stderr, fmt, ap); 362 va_end(ap); 363 (void) fflush(stdout); 364 (void) fflush(stderr); 365 dumpabort(); 366 } 367 368 #endif /* lint */ 369 370 /* 371 * Tell the operator what has to be done; 372 * we don't actually do it 373 */ 374 375 struct fstab * 376 allocfsent(fs) 377 register struct fstab *fs; 378 { 379 register struct fstab *new; 380 381 new = (struct fstab *)malloc(sizeof (*fs)); 382 if (new == NULL || 383 (new->fs_file = strdup(fs->fs_file)) == NULL || 384 (new->fs_type = strdup(fs->fs_type)) == NULL || 385 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 386 quit("%s\n", strerror(errno)); 387 new->fs_passno = fs->fs_passno; 388 new->fs_freq = fs->fs_freq; 389 return (new); 390 } 391 392 struct pfstab { 393 struct pfstab *pf_next; 394 struct fstab *pf_fstab; 395 }; 396 397 static struct pfstab *table; 398 399 void 400 getfstab() 401 { 402 register struct fstab *fs; 403 register struct pfstab *pf; 404 405 if (setfsent() == 0) { 406 msg("Can't open %s for dump table information: %s\n", 407 _PATH_FSTAB, strerror(errno)); 408 return; 409 } 410 while (fs = getfsent()) { 411 if (strcmp(fs->fs_type, FSTAB_RW) && 412 strcmp(fs->fs_type, FSTAB_RO) && 413 strcmp(fs->fs_type, FSTAB_RQ)) 414 continue; 415 fs = allocfsent(fs); 416 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 417 quit("%s\n", strerror(errno)); 418 pf->pf_fstab = fs; 419 pf->pf_next = table; 420 table = pf; 421 } 422 endfsent(); 423 } 424 425 /* 426 * Search in the fstab for a file name. 427 * This file name can be either the special or the path file name. 428 * 429 * The entries in the fstab are the BLOCK special names, not the 430 * character special names. 431 * The caller of fstabsearch assures that the character device 432 * is dumped (that is much faster) 433 * 434 * The file name can omit the leading '/'. 435 */ 436 struct fstab * 437 fstabsearch(key) 438 char *key; 439 { 440 register struct pfstab *pf; 441 register struct fstab *fs; 442 char *rawname(); 443 444 for (pf = table; pf != NULL; pf = pf->pf_next) { 445 fs = pf->pf_fstab; 446 if (strcmp(fs->fs_file, key) == 0 || 447 strcmp(fs->fs_spec, key) == 0 || 448 strcmp(rawname(fs->fs_spec), key) == 0) 449 return (fs); 450 if (key[0] != '/') { 451 if (*fs->fs_spec == '/' && 452 strcmp(fs->fs_spec + 1, key) == 0) 453 return (fs); 454 if (*fs->fs_file == '/' && 455 strcmp(fs->fs_file + 1, key) == 0) 456 return (fs); 457 } 458 } 459 return (NULL); 460 } 461 462 /* 463 * Tell the operator what to do 464 */ 465 void 466 lastdump(arg) 467 char arg; /* w ==> just what to do; W ==> most recent dumps */ 468 { 469 register int i; 470 register struct fstab *dt; 471 register struct dumpdates *dtwalk; 472 char *lastname, *date; 473 int dumpme, datesort(); 474 time_t tnow; 475 476 time(&tnow); 477 getfstab(); /* /etc/fstab input */ 478 initdumptimes(); /* /etc/dumpdates input */ 479 qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort); 480 481 if (arg == 'w') 482 (void) printf("Dump these file systems:\n"); 483 else 484 (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 485 lastname = "??"; 486 ITITERATE(i, dtwalk) { 487 if (strncmp(lastname, dtwalk->dd_name, 488 sizeof(dtwalk->dd_name)) == 0) 489 continue; 490 date = (char *)ctime(&dtwalk->dd_ddate); 491 date[16] = '\0'; /* blast away seconds and year */ 492 lastname = dtwalk->dd_name; 493 dt = fstabsearch(dtwalk->dd_name); 494 dumpme = (dt != NULL && 495 dt->fs_freq != 0 && 496 dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); 497 if (arg != 'w' || dumpme) 498 (void) printf( 499 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 500 dumpme && (arg != 'w') ? '>' : ' ', 501 dtwalk->dd_name, 502 dt ? dt->fs_file : "", 503 dtwalk->dd_level, 504 date); 505 } 506 } 507 508 int 509 datesort(a1, a2) 510 void *a1, *a2; 511 { 512 struct dumpdates *d1 = *(struct dumpdates **)a1; 513 struct dumpdates *d2 = *(struct dumpdates **)a2; 514 int diff; 515 516 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 517 if (diff == 0) 518 return (d2->dd_ddate - d1->dd_ddate); 519 return (diff); 520 } 521 522 int max(a, b) 523 int a, b; 524 { 525 return (a > b ? a : b); 526 } 527 int min(a, b) 528 int a, b; 529 { 530 return (a < b ? a : b); 531 } 532