1 /*- 2 * Copyright (c) 1980, 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; 36 #endif /* not lint */ 37 38 #include <sys/param.h> 39 #include <sys/wait.h> 40 #include <sys/time.h> 41 42 #include <errno.h> 43 #include <fstab.h> 44 #include <grp.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #ifdef __STDC__ 48 #include <stdlib.h> 49 #include <string.h> 50 #include <stdarg.h> 51 #endif 52 #include <tzfile.h> 53 #ifdef __STDC__ 54 #include <unistd.h> 55 #endif 56 #include <utmp.h> 57 #ifndef __STDC__ 58 #include <varargs.h> 59 #endif 60 61 #include "dump.h" 62 #include "pathnames.h" 63 64 void alarmcatch __P((/* int, int */)); 65 int datesort __P((const void *, const void *)); 66 static 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 */ 79 static int timeout; 80 static char *attnmessage; /* attention message */ 81 82 int 83 query(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 124 char 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 */ 130 void 131 alarmcatch() 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 */ 156 void 157 interrupt(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 */ 170 struct group *gp; 171 172 /* 173 * Get the names from the group entry "operator" to notify. 174 */ 175 void 176 set_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 189 struct 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 */ 195 void 196 broadcast(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 251 static void 252 sendmes(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\ 268 DUMP: 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 292 time_t tschedule = 0; 293 294 void 295 timeest() 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 313 void 314 #if __STDC__ 315 msg(const char *fmt, ...) 316 #else 317 msg(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 340 void 341 #if __STDC__ 342 msgtail(const char *fmt, ...) 343 #else 344 msgtail(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 359 void 360 #if __STDC__ 361 quit(const char *fmt, ...) 362 #else 363 quit(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 391 struct fstab * 392 allocfsent(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 408 struct pfstab { 409 struct pfstab *pf_next; 410 struct fstab *pf_fstab; 411 }; 412 413 static struct pfstab *table; 414 415 void 416 getfstab() 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 */ 452 struct fstab * 453 fstabsearch(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 */ 483 void 484 lastdump(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 526 int 527 datesort(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