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.14 (Berkeley) 07/16/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, sig) == 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(signo) 135 int signo; 136 { 137 msg("Interrupt received.\n"); 138 if (query("Do you want to abort dump?")) 139 dumpabort(0); 140 } 141 142 /* 143 * The following variables and routines manage alerting 144 * operators to the status of dump. 145 * This works much like wall(1) does. 146 */ 147 struct group *gp; 148 149 /* 150 * Get the names from the group entry "operator" to notify. 151 */ 152 void 153 set_operators() 154 { 155 if (!notify) /*not going to notify*/ 156 return; 157 gp = getgrnam(OPGRENT); 158 (void) endgrent(); 159 if (gp == NULL) { 160 msg("No group entry for %s.\n", OPGRENT); 161 notify = 0; 162 return; 163 } 164 } 165 166 struct tm *localtime(); 167 struct tm *localclock; 168 169 /* 170 * We fork a child to do the actual broadcasting, so 171 * that the process control groups are not messed up 172 */ 173 void 174 broadcast(message) 175 char *message; 176 { 177 time_t clock; 178 FILE *f_utmp; 179 struct utmp utmp; 180 char **np; 181 int pid, s; 182 183 if (!notify || gp == NULL) 184 return; 185 186 switch (pid = fork()) { 187 case -1: 188 return; 189 case 0: 190 break; 191 default: 192 while (wait(&s) != pid) 193 continue; 194 return; 195 } 196 197 clock = time((time_t *)0); 198 localclock = localtime(&clock); 199 200 if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 201 msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 202 return; 203 } 204 205 while (!feof(f_utmp)) { 206 if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1) 207 break; 208 if (utmp.ut_name[0] == 0) 209 continue; 210 for (np = gp->gr_mem; *np; np++) { 211 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 212 continue; 213 /* 214 * Do not send messages to operators on dialups 215 */ 216 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 217 continue; 218 #ifdef DEBUG 219 msg("Message to %s at %s\n", *np, utmp.ut_line); 220 #endif 221 sendmes(utmp.ut_line, message); 222 } 223 } 224 (void) fclose(f_utmp); 225 Exit(0); /* the wait in this same routine will catch this */ 226 /* NOTREACHED */ 227 } 228 229 static void 230 sendmes(tty, message) 231 char *tty, *message; 232 { 233 char t[50], buf[BUFSIZ]; 234 register char *cp; 235 int lmsg = 1; 236 FILE *f_tty; 237 238 (void) strcpy(t, _PATH_DEV); 239 (void) strcat(t, tty); 240 241 if ((f_tty = fopen(t, "w")) != NULL) { 242 setbuf(f_tty, buf); 243 (void) fprintf(f_tty, 244 "\n\ 245 \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 246 DUMP: NEEDS ATTENTION: ", 247 localclock->tm_hour, localclock->tm_min); 248 for (cp = lastmsg; ; cp++) { 249 if (*cp == '\0') { 250 if (lmsg) { 251 cp = message; 252 if (*cp == '\0') 253 break; 254 lmsg = 0; 255 } else 256 break; 257 } 258 if (*cp == '\n') 259 (void) putc('\r', f_tty); 260 (void) putc(*cp, f_tty); 261 } 262 (void) fclose(f_tty); 263 } 264 } 265 266 /* 267 * print out an estimate of the amount of time left to do the dump 268 */ 269 270 time_t tschedule = 0; 271 272 void 273 timeest() 274 { 275 time_t tnow, deltat; 276 277 (void) time((time_t *) &tnow); 278 if (tnow >= tschedule) { 279 tschedule = tnow + 300; 280 if (blockswritten < 500) 281 return; 282 deltat = tstart_writing - tnow + 283 (1.0 * (tnow - tstart_writing)) 284 / blockswritten * tapesize; 285 msg("%3.2f%% done, finished in %d:%02d\n", 286 (blockswritten * 100.0) / tapesize, 287 deltat / 3600, (deltat % 3600) / 60); 288 } 289 } 290 291 void 292 #if __STDC__ 293 msg(const char *fmt, ...) 294 #else 295 msg(fmt, va_alist) 296 char *fmt; 297 va_dcl 298 #endif 299 { 300 va_list ap; 301 302 (void) fprintf(stderr," DUMP: "); 303 #ifdef TDEBUG 304 (void) fprintf(stderr, "pid=%d ", getpid()); 305 #endif 306 #if __STDC__ 307 va_start(ap, fmt); 308 #else 309 va_start(ap); 310 #endif 311 (void) vfprintf(stderr, fmt, ap); 312 (void) fflush(stdout); 313 (void) fflush(stderr); 314 (void) vsprintf(lastmsg, fmt, ap); 315 va_end(ap); 316 } 317 318 void 319 #if __STDC__ 320 msgtail(const char *fmt, ...) 321 #else 322 msgtail(fmt, va_alist) 323 char *fmt; 324 va_dcl 325 #endif 326 { 327 va_list ap; 328 #if __STDC__ 329 va_start(ap, fmt); 330 #else 331 va_start(ap); 332 #endif 333 (void) vfprintf(stderr, fmt, ap); 334 va_end(ap); 335 } 336 337 void 338 #if __STDC__ 339 quit(const char *fmt, ...) 340 #else 341 quit(fmt, va_alist) 342 char *fmt; 343 va_dcl 344 #endif 345 { 346 va_list ap; 347 348 (void) fprintf(stderr," DUMP: "); 349 #ifdef TDEBUG 350 (void) fprintf(stderr, "pid=%d ", getpid()); 351 #endif 352 #if __STDC__ 353 va_start(ap, fmt); 354 #else 355 va_start(ap); 356 #endif 357 (void) vfprintf(stderr, fmt, ap); 358 va_end(ap); 359 (void) fflush(stdout); 360 (void) fflush(stderr); 361 dumpabort(0); 362 } 363 364 /* 365 * Tell the operator what has to be done; 366 * we don't actually do it 367 */ 368 369 struct fstab * 370 allocfsent(fs) 371 register struct fstab *fs; 372 { 373 register struct fstab *new; 374 375 new = (struct fstab *)malloc(sizeof (*fs)); 376 if (new == NULL || 377 (new->fs_file = strdup(fs->fs_file)) == NULL || 378 (new->fs_type = strdup(fs->fs_type)) == NULL || 379 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 380 quit("%s\n", strerror(errno)); 381 new->fs_passno = fs->fs_passno; 382 new->fs_freq = fs->fs_freq; 383 return (new); 384 } 385 386 struct pfstab { 387 struct pfstab *pf_next; 388 struct fstab *pf_fstab; 389 }; 390 391 static struct pfstab *table; 392 393 void 394 getfstab() 395 { 396 register struct fstab *fs; 397 register struct pfstab *pf; 398 399 if (setfsent() == 0) { 400 msg("Can't open %s for dump table information: %s\n", 401 _PATH_FSTAB, strerror(errno)); 402 return; 403 } 404 while (fs = getfsent()) { 405 if (strcmp(fs->fs_type, FSTAB_RW) && 406 strcmp(fs->fs_type, FSTAB_RO) && 407 strcmp(fs->fs_type, FSTAB_RQ)) 408 continue; 409 fs = allocfsent(fs); 410 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 411 quit("%s\n", strerror(errno)); 412 pf->pf_fstab = fs; 413 pf->pf_next = table; 414 table = pf; 415 } 416 (void) endfsent(); 417 } 418 419 /* 420 * Search in the fstab for a file name. 421 * This file name can be either the special or the path file name. 422 * 423 * The entries in the fstab are the BLOCK special names, not the 424 * character special names. 425 * The caller of fstabsearch assures that the character device 426 * is dumped (that is much faster) 427 * 428 * The file name can omit the leading '/'. 429 */ 430 struct fstab * 431 fstabsearch(key) 432 char *key; 433 { 434 register struct pfstab *pf; 435 register struct fstab *fs; 436 char *rawname(); 437 438 for (pf = table; pf != NULL; pf = pf->pf_next) { 439 fs = pf->pf_fstab; 440 if (strcmp(fs->fs_file, key) == 0 || 441 strcmp(fs->fs_spec, key) == 0 || 442 strcmp(rawname(fs->fs_spec), key) == 0) 443 return (fs); 444 if (key[0] != '/') { 445 if (*fs->fs_spec == '/' && 446 strcmp(fs->fs_spec + 1, key) == 0) 447 return (fs); 448 if (*fs->fs_file == '/' && 449 strcmp(fs->fs_file + 1, key) == 0) 450 return (fs); 451 } 452 } 453 return (NULL); 454 } 455 456 /* 457 * Tell the operator what to do 458 */ 459 void 460 lastdump(arg) 461 char arg; /* w ==> just what to do; W ==> most recent dumps */ 462 { 463 register int i; 464 register struct fstab *dt; 465 register struct dumpdates *dtwalk; 466 char *lastname, *date; 467 int dumpme, datesort(); 468 time_t tnow; 469 470 (void) time(&tnow); 471 getfstab(); /* /etc/fstab input */ 472 initdumptimes(); /* /etc/dumpdates input */ 473 qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); 474 475 if (arg == 'w') 476 (void) printf("Dump these file systems:\n"); 477 else 478 (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 479 lastname = "??"; 480 ITITERATE(i, dtwalk) { 481 if (strncmp(lastname, dtwalk->dd_name, 482 sizeof(dtwalk->dd_name)) == 0) 483 continue; 484 date = (char *)ctime(&dtwalk->dd_ddate); 485 date[16] = '\0'; /* blast away seconds and year */ 486 lastname = dtwalk->dd_name; 487 dt = fstabsearch(dtwalk->dd_name); 488 dumpme = (dt != NULL && 489 dt->fs_freq != 0 && 490 dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); 491 if (arg != 'w' || dumpme) 492 (void) printf( 493 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 494 dumpme && (arg != 'w') ? '>' : ' ', 495 dtwalk->dd_name, 496 dt ? dt->fs_file : "", 497 dtwalk->dd_level, 498 date); 499 } 500 } 501 502 int 503 datesort(a1, a2) 504 void *a1, *a2; 505 { 506 struct dumpdates *d1 = *(struct dumpdates **)a1; 507 struct dumpdates *d2 = *(struct dumpdates **)a2; 508 int diff; 509 510 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 511 if (diff == 0) 512 return (d2->dd_ddate - d1->dd_ddate); 513 return (diff); 514 } 515 516