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