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