1 /* $NetBSD: optr.c,v 1.17 2001/05/28 01:09:55 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1980, 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)optr.c 8.2 (Berkeley) 1/6/94"; 40 #else 41 __RCSID("$NetBSD: optr.c,v 1.17 2001/05/28 01:09:55 lukem Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/wait.h> 47 #include <sys/time.h> 48 #include <sys/ucred.h> 49 #include <sys/mount.h> 50 51 #include <errno.h> 52 #include <fstab.h> 53 #include <grp.h> 54 #include <signal.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <stdarg.h> 59 #include <time.h> 60 #include <tzfile.h> 61 #include <unistd.h> 62 #include <utmp.h> 63 64 #include "dump.h" 65 #include "pathnames.h" 66 67 void alarmcatch(int); 68 struct fstab *allocfsent(struct fstab *); 69 int datesort(const void *, const void *); 70 static void sendmes(char *, char *); 71 72 /* 73 * Query the operator; This previously-fascist piece of code 74 * no longer requires an exact response. 75 * It is intended to protect dump aborting by inquisitive 76 * people banging on the console terminal to see what is 77 * happening which might cause dump to croak, destroying 78 * a large number of hours of work. 79 * 80 * Every 2 minutes we reprint the message, alerting others 81 * that dump needs attention. 82 */ 83 static int timeout; 84 static char *attnmessage; /* attention message */ 85 86 int 87 query(char *question) 88 { 89 char replybuffer[64]; 90 int back, errcount; 91 FILE *mytty; 92 time_t firstprompt, when_answered; 93 94 firstprompt = time((time_t *)0); 95 96 if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 97 quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 98 attnmessage = question; 99 timeout = 0; 100 alarmcatch(0); 101 back = -1; 102 errcount = 0; 103 do { 104 if (fgets(replybuffer, 63, mytty) == NULL) { 105 clearerr(mytty); 106 if (++errcount > 30) /* XXX ugly */ 107 quit("excessive operator query failures\n"); 108 } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 109 back = 1; 110 } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 111 back = 0; 112 } else { 113 (void) fprintf(stderr, 114 " DUMP: \"Yes\" or \"No\"?\n"); 115 (void) fprintf(stderr, 116 " DUMP: %s: (\"yes\" or \"no\") ", question); 117 } 118 } while (back < 0); 119 120 /* 121 * Turn off the alarm, and reset the signal to trap out.. 122 */ 123 (void) alarm(0); 124 if (signal(SIGALRM, sig) == SIG_IGN) 125 signal(SIGALRM, SIG_IGN); 126 (void) fclose(mytty); 127 when_answered = time((time_t *)0); 128 /* 129 * Adjust the base for time estimates to ignore time we spent waiting 130 * for operator input. 131 */ 132 if (tstart_writing != 0) 133 tstart_writing += (when_answered - firstprompt); 134 return(back); 135 } 136 137 char lastmsg[100]; 138 139 /* 140 * Alert the console operator, and enable the alarm clock to 141 * sleep for 2 minutes in case nobody comes to satisfy dump 142 */ 143 void 144 alarmcatch(int dummy) 145 { 146 147 if (notify == 0) { 148 if (timeout == 0) 149 (void) fprintf(stderr, 150 " DUMP: %s: (\"yes\" or \"no\") ", 151 attnmessage); 152 else 153 msgtail("\7\7"); 154 } else { 155 if (timeout) { 156 msgtail("\n"); 157 broadcast(""); /* just print last msg */ 158 } 159 (void) fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", 160 attnmessage); 161 } 162 signal(SIGALRM, alarmcatch); 163 (void) alarm(120); 164 timeout = 1; 165 } 166 167 /* 168 * Here if an inquisitive operator interrupts the dump program 169 */ 170 void 171 interrupt(int signo) 172 { 173 174 msg("Interrupt received.\n"); 175 if (query("Do you want to abort dump?")) 176 dumpabort(0); 177 } 178 179 /* 180 * The following variables and routines manage alerting 181 * operators to the status of dump. 182 * This works much like wall(1) does. 183 */ 184 struct group *gp; 185 186 /* 187 * Get the names from the group entry "operator" to notify. 188 */ 189 void 190 set_operators(void) 191 { 192 193 if (!notify) /*not going to notify*/ 194 return; 195 gp = getgrnam(OPGRENT); 196 (void) endgrent(); 197 if (gp == NULL) { 198 msg("No group entry for %s.\n", OPGRENT); 199 notify = 0; 200 return; 201 } 202 } 203 204 struct tm *localclock; 205 206 /* 207 * We fork a child to do the actual broadcasting, so 208 * that the process control groups are not messed up 209 */ 210 void 211 broadcast(char *message) 212 { 213 time_t clock; 214 FILE *f_utmp; 215 struct utmp utmp; 216 char **np; 217 int pid, s; 218 219 if (!notify || gp == NULL) 220 return; 221 222 switch (pid = fork()) { 223 case -1: 224 return; 225 case 0: 226 break; 227 default: 228 while (wait(&s) != pid) 229 continue; 230 return; 231 } 232 233 clock = time((time_t *)0); 234 localclock = localtime(&clock); 235 236 if ((f_utmp = fopen(_PATH_UTMP, "r")) == NULL) { 237 msg("Cannot open %s: %s\n", _PATH_UTMP, strerror(errno)); 238 return; 239 } 240 241 while (!feof(f_utmp)) { 242 if (fread((char *) &utmp, sizeof (struct utmp), 1, f_utmp) != 1) 243 break; 244 if (utmp.ut_name[0] == 0) 245 continue; 246 for (np = gp->gr_mem; *np; np++) { 247 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 248 continue; 249 /* 250 * Do not send messages to operators on dialups 251 */ 252 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 253 continue; 254 #ifdef DEBUG 255 msg("Message to %s at %s\n", *np, utmp.ut_line); 256 #endif 257 sendmes(utmp.ut_line, message); 258 } 259 } 260 (void) fclose(f_utmp); 261 Exit(0); /* the wait in this same routine will catch this */ 262 /* NOTREACHED */ 263 } 264 265 static void 266 sendmes(char *tty, char *message) 267 { 268 char t[50], buf[BUFSIZ]; 269 char *cp; 270 int lmsg = 1; 271 FILE *f_tty; 272 273 (void)strncpy(t, _PATH_DEV, sizeof(t) - 1); 274 (void)strncat(t, tty, sizeof(t) - sizeof(_PATH_DEV) - 1); 275 t[sizeof(t) - 1] = '\0'; 276 277 if ((f_tty = fopen(t, "w")) != NULL) { 278 setbuf(f_tty, buf); 279 (void) fprintf(f_tty, 280 "\n\ 281 \7\7\7Message from the dump program to all operators at %d:%02d ...\r\n\n\ 282 DUMP: NEEDS ATTENTION: ", 283 localclock->tm_hour, localclock->tm_min); 284 for (cp = lastmsg; ; cp++) { 285 if (*cp == '\0') { 286 if (lmsg) { 287 cp = message; 288 if (*cp == '\0') 289 break; 290 lmsg = 0; 291 } else 292 break; 293 } 294 if (*cp == '\n') 295 (void) putc('\r', f_tty); 296 (void) putc(*cp, f_tty); 297 } 298 (void) fclose(f_tty); 299 } 300 } 301 302 /* 303 * print out an estimate of the amount of time left to do the dump 304 */ 305 306 time_t tschedule = 0; 307 308 void 309 timeest(void) 310 { 311 time_t tnow, deltat; 312 313 (void) time((time_t *) &tnow); 314 if (tnow >= tschedule) { 315 tschedule = tnow + 300; 316 if (blockswritten < 500) 317 return; 318 deltat = tstart_writing - tnow + 319 (1.0 * (tnow - tstart_writing)) 320 / blockswritten * tapesize; 321 msg("%3.2f%% done, finished in %ld:%02ld\n", 322 (blockswritten * 100.0) / tapesize, 323 (long)(deltat / 3600), (long)((deltat % 3600) / 60)); 324 } 325 } 326 327 void 328 msg(const char *fmt, ...) 329 { 330 va_list ap; 331 332 (void) fprintf(stderr," DUMP: "); 333 #ifdef TDEBUG 334 (void) fprintf(stderr, "pid=%d ", getpid()); 335 #endif 336 va_start(ap, fmt); 337 (void) vfprintf(stderr, fmt, ap); 338 (void) fflush(stdout); 339 (void) fflush(stderr); 340 (void) vsnprintf(lastmsg, sizeof lastmsg, fmt, ap); 341 va_end(ap); 342 } 343 344 void 345 msgtail(const char *fmt, ...) 346 { 347 va_list ap; 348 349 va_start(ap, fmt); 350 (void) vfprintf(stderr, fmt, ap); 351 va_end(ap); 352 } 353 354 void 355 quit(const char *fmt, ...) 356 { 357 va_list ap; 358 359 (void) fprintf(stderr," DUMP: "); 360 #ifdef TDEBUG 361 (void) fprintf(stderr, "pid=%d ", getpid()); 362 #endif 363 va_start(ap, fmt); 364 (void) vfprintf(stderr, fmt, ap); 365 va_end(ap); 366 (void) fflush(stdout); 367 (void) fflush(stderr); 368 dumpabort(0); 369 } 370 371 /* 372 * Tell the operator what has to be done; 373 * we don't actually do it 374 */ 375 376 struct fstab * 377 allocfsent(struct fstab *fs) 378 { 379 struct fstab *new; 380 381 new = (struct fstab *)xmalloc(sizeof (*fs)); 382 new->fs_file = xstrdup(fs->fs_file); 383 new->fs_type = xstrdup(fs->fs_type); 384 new->fs_spec = xstrdup(fs->fs_spec); 385 new->fs_passno = fs->fs_passno; 386 new->fs_freq = fs->fs_freq; 387 return (new); 388 } 389 390 struct pfstab { 391 struct pfstab *pf_next; 392 struct fstab *pf_fstab; 393 }; 394 395 static struct pfstab *table; 396 397 void 398 getfstab(void) 399 { 400 struct fstab *fs; 401 struct pfstab *pf; 402 403 if (setfsent() == 0) { 404 msg("Can't open %s for dump table information: %s\n", 405 _PATH_FSTAB, strerror(errno)); 406 return; 407 } 408 while ((fs = getfsent()) != NULL) { 409 if (strcmp(fs->fs_type, FSTAB_RW) && 410 strcmp(fs->fs_type, FSTAB_RO) && 411 strcmp(fs->fs_type, FSTAB_RQ)) 412 continue; 413 if (strcmp(fs->fs_vfstype, "ufs") && 414 strcmp(fs->fs_vfstype, "ffs")) 415 continue; 416 fs = allocfsent(fs); 417 pf = (struct pfstab *)xmalloc(sizeof (*pf)); 418 pf->pf_fstab = fs; 419 pf->pf_next = table; 420 table = pf; 421 } 422 (void) 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(const char *key) 438 { 439 struct pfstab *pf; 440 struct fstab *fs; 441 char *rn; 442 443 for (pf = table; pf != NULL; pf = pf->pf_next) { 444 fs = pf->pf_fstab; 445 if (strcmp(fs->fs_file, key) == 0 || 446 strcmp(fs->fs_spec, key) == 0) 447 return (fs); 448 rn = rawname(fs->fs_spec); 449 if (rn != NULL && strcmp(rn, key) == 0) 450 return (fs); 451 if (key[0] != '/') { 452 if (*fs->fs_spec == '/' && 453 strcmp(fs->fs_spec + 1, key) == 0) 454 return (fs); 455 if (*fs->fs_file == '/' && 456 strcmp(fs->fs_file + 1, key) == 0) 457 return (fs); 458 } 459 } 460 return (NULL); 461 } 462 463 /* 464 * Search in the mounted file list for a file name. 465 * This file name can be either the special or the path file name. 466 * 467 * The entries in the list are the BLOCK special names, not the 468 * character special names. 469 * The caller of mntinfosearch assures that the character device 470 * is dumped (that is much faster) 471 */ 472 struct statfs * 473 mntinfosearch(const char *key) 474 { 475 int i, mntbufc; 476 struct statfs *mntbuf, *fs; 477 char *rn; 478 479 if ((mntbufc = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) 480 quit("Can't get mount list: %s", strerror(errno)); 481 for (fs = mntbuf, i = 0; i < mntbufc; i++, fs++) { 482 if (strcmp(fs->f_fstypename, "ufs") != 0 && 483 strcmp(fs->f_fstypename, "ffs") != 0) 484 continue; 485 if (strcmp(fs->f_mntonname, key) == 0 || 486 strcmp(fs->f_mntfromname, key) == 0) 487 return (fs); 488 rn = rawname(fs->f_mntfromname); 489 if (rn != NULL && strcmp(rn, key) == 0) 490 return (fs); 491 } 492 return (NULL); 493 } 494 495 496 /* 497 * Tell the operator what to do. 498 * arg: w ==> just what to do; W ==> most recent dumps 499 */ 500 void 501 lastdump(char arg) 502 { 503 int i; 504 struct fstab *dt; 505 struct dumpdates *dtwalk; 506 char *lastname, *date; 507 int dumpme; 508 time_t tnow; 509 510 (void) time(&tnow); 511 getfstab(); /* /etc/fstab input */ 512 initdumptimes(); /* /etc/dumpdates input */ 513 qsort((char *) ddatev, nddates, sizeof(struct dumpdates *), datesort); 514 515 if (arg == 'w') 516 (void) printf("Dump these file systems:\n"); 517 else 518 (void) printf("Last dump(s) done (Dump '>' file systems):\n"); 519 lastname = "??"; 520 ITITERATE(i, dtwalk) { 521 if (strncmp(lastname, dtwalk->dd_name, 522 sizeof(dtwalk->dd_name)) == 0) 523 continue; 524 date = (char *)ctime(&dtwalk->dd_ddate); 525 date[24] = '\0'; 526 strcpy(date + 16, date + 19); /* blast away seconds */ 527 lastname = dtwalk->dd_name; 528 dt = fstabsearch(dtwalk->dd_name); 529 dumpme = (dt != NULL && 530 dt->fs_freq != 0 && 531 dtwalk->dd_ddate < tnow - (dt->fs_freq * SECSPERDAY)); 532 if (arg != 'w' || dumpme) 533 (void) printf( 534 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 535 dumpme && (arg != 'w') ? '>' : ' ', 536 dtwalk->dd_name, 537 dt ? dt->fs_file : "", 538 dtwalk->dd_level, 539 date); 540 } 541 } 542 543 int 544 datesort(const void *a1, const void *a2) 545 { 546 struct dumpdates *d1 = *(struct dumpdates **)a1; 547 struct dumpdates *d2 = *(struct dumpdates **)a2; 548 int diff; 549 550 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 551 if (diff == 0) 552 return (d2->dd_ddate - d1->dd_ddate); 553 return (diff); 554 } 555