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 * @(#)optr.c 8.2 (Berkeley) 1/6/94 34 * $FreeBSD: src/sbin/dump/optr.c,v 1.9.2.5 2002/02/23 22:32:51 iedowse Exp $ 35 * $DragonFly: src/sbin/dump/optr.c,v 1.9 2005/04/13 15:45:37 joerg Exp $ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/queue.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 <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <stdarg.h> 50 #include <signal.h> 51 #include <unistd.h> 52 #include <utmp.h> 53 54 #include "dump.h" 55 #include "pathnames.h" 56 57 static void alarmcatch(int); 58 static int datesort(const void *, const void *); 59 60 /* 61 * Query the operator; This previously-fascist piece of code 62 * no longer requires an exact response. 63 * It is intended to protect dump aborting by inquisitive 64 * people banging on the console terminal to see what is 65 * happening which might cause dump to croak, destroying 66 * a large number of hours of work. 67 * 68 * Every 2 minutes we reprint the message, alerting others 69 * that dump needs attention. 70 */ 71 static int timeout; 72 static const char *attnmessage; /* attention message */ 73 74 int 75 query(const char *question) 76 { 77 char replybuffer[64]; 78 int back, errcount; 79 FILE *mytty; 80 81 if ((mytty = fopen(_PATH_TTY, "r")) == NULL) 82 quit("fopen on %s fails: %s\n", _PATH_TTY, strerror(errno)); 83 attnmessage = question; 84 timeout = 0; 85 alarmcatch(0); 86 back = -1; 87 errcount = 0; 88 do { 89 if (fgets(replybuffer, 63, mytty) == NULL) { 90 clearerr(mytty); 91 if (++errcount > 30) /* XXX ugly */ 92 quit("excessive operator query failures\n"); 93 } else if (replybuffer[0] == 'y' || replybuffer[0] == 'Y') { 94 back = 1; 95 } else if (replybuffer[0] == 'n' || replybuffer[0] == 'N') { 96 back = 0; 97 } else { 98 fprintf(stderr, " DUMP: \"Yes\" or \"No\"?\n"); 99 fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", 100 question); 101 } 102 } while (back < 0); 103 104 /* 105 * Turn off the alarm, and reset the signal to trap out.. 106 */ 107 alarm(0); 108 if (signal(SIGALRM, sig) == SIG_IGN) 109 signal(SIGALRM, SIG_IGN); 110 fclose(mytty); 111 return(back); 112 } 113 114 char lastmsg[BUFSIZ]; 115 116 /* 117 * Alert the console operator, and enable the alarm clock to 118 * sleep for 2 minutes in case nobody comes to satisfy dump 119 */ 120 static void 121 alarmcatch(int signo __unused) 122 { 123 if (notify == 0) { 124 if (timeout == 0) 125 fprintf(stderr, " DUMP: %s: (\"yes\" or \"no\") ", 126 attnmessage); 127 else 128 msgtail("\a\a"); 129 } else { 130 if (timeout) { 131 msgtail("\n"); 132 broadcast(""); /* just print last msg */ 133 } 134 fprintf(stderr," DUMP: %s: (\"yes\" or \"no\") ", attnmessage); 135 } 136 signal(SIGALRM, alarmcatch); 137 alarm(120); 138 timeout = 1; 139 } 140 141 /* 142 * Here if an inquisitive operator interrupts the dump program 143 */ 144 void 145 interrupt(int signo __unused) 146 { 147 msg("Interrupt received.\n"); 148 if (query("Do you want to abort dump?")) 149 dumpabort(0); 150 } 151 152 /* 153 * We now use wall(1) to do the actual broadcasting. 154 */ 155 void 156 broadcast(const char *message) 157 { 158 FILE *fp; 159 char buf[sizeof(_PATH_WALL) + sizeof(OPGRENT) + 3]; 160 161 if (!notify) 162 return; 163 164 snprintf(buf, sizeof(buf), "%s -g %s", _PATH_WALL, OPGRENT); 165 if ((fp = popen(buf, "w")) == NULL) 166 return; 167 168 fputs("\a\a\aMessage from the dump program to all operators\n\nDUMP: NEEDS ATTENTION: ", fp); 169 if (lastmsg[0]) 170 fputs(lastmsg, fp); 171 if (message[0]) 172 fputs(message, fp); 173 174 pclose(fp); 175 } 176 177 /* 178 * Print out an estimate of the amount of time left to do the dump 179 */ 180 181 time_t tschedule = 0; 182 183 void 184 timeest(void) 185 { 186 double percent; 187 time_t tnow; 188 int deltat, hours, mins; 189 190 time(&tnow); 191 deltat = (blockswritten == 0) ? 0 : tstart_writing - tnow + 192 (double)(tnow - tstart_writing) / blockswritten * tapesize; 193 percent = (blockswritten * 100.0) / tapesize; 194 hours = deltat / 3600; 195 mins = (deltat % 3600) / 60; 196 197 setproctitle("%s: pass %d: %3.2f%% done, finished in %d:%02d", 198 disk, passno, percent, hours, mins); 199 if (tnow >= tschedule) { 200 tschedule = tnow + 300; 201 if (blockswritten < 500) 202 return; 203 msg("%3.2f%% done, finished in %d:%02d\n", percent, hours, 204 mins); 205 } 206 } 207 208 /* 209 * Schedule a printout of the estimate in the next call to timeest(). 210 */ 211 void 212 infosch(int signo __unused) 213 { 214 tschedule = 0; 215 } 216 217 void 218 msg(const char *fmt, ...) 219 { 220 va_list ap; 221 222 fprintf(stderr," DUMP: "); 223 #ifdef TDEBUG 224 fprintf(stderr, "pid=%d ", getpid()); 225 #endif 226 va_start(ap, fmt); 227 vfprintf(stderr, fmt, ap); 228 fflush(stdout); 229 fflush(stderr); 230 vsnprintf(lastmsg, sizeof(lastmsg), fmt, ap); 231 va_end(ap); 232 } 233 234 void 235 msgtail(const char *fmt, ...) 236 { 237 va_list ap; 238 239 va_start(ap, fmt); 240 vfprintf(stderr, fmt, ap); 241 va_end(ap); 242 } 243 244 void 245 quit(const char *fmt, ...) 246 { 247 va_list ap; 248 249 fprintf(stderr," DUMP: "); 250 #ifdef TDEBUG 251 fprintf(stderr, "pid=%d ", getpid()); 252 #endif 253 va_start(ap, fmt); 254 vfprintf(stderr, fmt, ap); 255 va_end(ap); 256 fflush(stdout); 257 fflush(stderr); 258 dumpabort(0); 259 } 260 261 /* 262 * Tell the operator what has to be done; 263 * we don't actually do it 264 */ 265 266 static struct fstab * 267 allocfsent(struct fstab *fs) 268 { 269 struct fstab *new; 270 271 new = (struct fstab *)malloc(sizeof (*fs)); 272 if (new == NULL || 273 (new->fs_file = strdup(fs->fs_file)) == NULL || 274 (new->fs_type = strdup(fs->fs_type)) == NULL || 275 (new->fs_spec = strdup(fs->fs_spec)) == NULL) 276 quit("%s\n", strerror(errno)); 277 new->fs_passno = fs->fs_passno; 278 new->fs_freq = fs->fs_freq; 279 return (new); 280 } 281 282 struct pfstab { 283 SLIST_ENTRY(pfstab) pf_list; 284 struct fstab *pf_fstab; 285 }; 286 287 static SLIST_HEAD(, pfstab) table; 288 289 void 290 getfstab(void) 291 { 292 struct fstab *fs; 293 struct pfstab *pf; 294 295 if (setfsent() == 0) { 296 msg("Can't open %s for dump table information: %s\n", 297 _PATH_FSTAB, strerror(errno)); 298 return; 299 } 300 while ((fs = getfsent()) != NULL) { 301 if (strcmp(fs->fs_type, FSTAB_RW) && 302 strcmp(fs->fs_type, FSTAB_RO) && 303 strcmp(fs->fs_type, FSTAB_RQ)) 304 continue; 305 fs = allocfsent(fs); 306 if ((pf = (struct pfstab *)malloc(sizeof (*pf))) == NULL) 307 quit("%s\n", strerror(errno)); 308 pf->pf_fstab = fs; 309 SLIST_INSERT_HEAD(&table, pf, pf_list); 310 } 311 endfsent(); 312 } 313 314 /* 315 * Search in the fstab for a file name. 316 * This file name can be either the special or the path file name. 317 * 318 * The file name can omit the leading '/'. 319 */ 320 struct fstab * 321 fstabsearch(const char *key) 322 { 323 struct pfstab *pf; 324 struct fstab *fs; 325 char *rn; 326 327 SLIST_FOREACH(pf, &table, pf_list) { 328 fs = pf->pf_fstab; 329 if (strcmp(fs->fs_file, key) == 0 || 330 strcmp(fs->fs_spec, key) == 0) 331 return (fs); 332 rn = rawname(fs->fs_spec); 333 if (rn != NULL && strcmp(rn, key) == 0) 334 return (fs); 335 if (key[0] != '/') { 336 if (*fs->fs_spec == '/' && 337 strcmp(fs->fs_spec + 1, key) == 0) 338 return (fs); 339 if (*fs->fs_file == '/' && 340 strcmp(fs->fs_file + 1, key) == 0) 341 return (fs); 342 } 343 } 344 return (NULL); 345 } 346 347 /* 348 * Tell the operator what to do 349 * 350 * char arg: w ==> just what to do; W ==> most recent dumps 351 */ 352 void 353 lastdump(int arg) 354 { 355 int i; 356 struct fstab *dt; 357 struct dumpdates *dtwalk; 358 const char *lastname; 359 char *date; 360 int dumpme; 361 time_t tnow; 362 struct tm *tlast; 363 364 time(&tnow); 365 getfstab(); /* /etc/fstab input */ 366 initdumptimes(); /* /etc/dumpdates input */ 367 qsort(ddatev, nddates, sizeof(struct dumpdates *), datesort); 368 369 if (arg == 'w') 370 printf("Dump these file systems:\n"); 371 else 372 printf("Last dump(s) done (Dump '>' file systems):\n"); 373 lastname = "??"; 374 ITITERATE(i, dtwalk) { 375 if (strncmp(lastname, dtwalk->dd_name, 376 sizeof(dtwalk->dd_name)) == 0) 377 continue; 378 date = (char *)ctime(&dtwalk->dd_ddate); 379 date[16] = '\0'; /* blast away seconds and year */ 380 lastname = dtwalk->dd_name; 381 dt = fstabsearch(dtwalk->dd_name); 382 dumpme = (dt != NULL && dt->fs_freq != 0); 383 if (dumpme) { 384 tlast = localtime(&dtwalk->dd_ddate); 385 dumpme = tnow > (dtwalk->dd_ddate - (tlast->tm_hour * 3600) 386 - (tlast->tm_min * 60) - tlast->tm_sec 387 + (dt->fs_freq * 86400)); 388 }; 389 if (arg != 'w' || dumpme) 390 printf( 391 "%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 392 dumpme && (arg != 'w') ? '>' : ' ', 393 dtwalk->dd_name, 394 dt ? dt->fs_file : "", 395 dtwalk->dd_level, 396 date); 397 } 398 } 399 400 static int 401 datesort(const void *a1, const void *a2) 402 { 403 const struct dumpdates *d1 = *(const struct dumpdates * const *)a1; 404 const struct dumpdates *d2 = *(const struct dumpdates * const *)a2; 405 int diff; 406 407 diff = strncmp(d1->dd_name, d2->dd_name, sizeof(d1->dd_name)); 408 if (diff == 0) 409 return (d2->dd_ddate - d1->dd_ddate); 410 return (diff); 411 } 412