1 static char *sccsid = "@(#)optr.c 1.8 (Berkeley) 06/19/83"; 2 3 #include "dump.h" 4 5 /* 6 * This is from /usr/include/grp.h 7 * That defined struct group, which conflicts 8 * with the struct group defined in param.h 9 */ 10 struct Group { /* see getgrent(3) */ 11 char *gr_name; 12 char *gr_passwd; 13 int gr_gid; 14 char **gr_mem; 15 }; 16 struct Group *getgrnam(); 17 /* 18 * Query the operator; This fascist piece of code requires 19 * an exact response. 20 * It is intended to protect dump aborting by inquisitive 21 * people banging on the console terminal to see what is 22 * happening which might cause dump to croak, destroying 23 * a large number of hours of work. 24 * 25 * Every 2 minutes we reprint the message, alerting others 26 * that dump needs attention. 27 */ 28 int timeout; 29 char *attnmessage; /* attemtion message */ 30 query(question) 31 char *question; 32 { 33 char replybuffer[64]; 34 int back; 35 FILE *mytty; 36 37 if ( (mytty = fopen("/dev/tty", "r")) == NULL){ 38 msg("fopen on /dev/tty fails\n"); 39 abort(); 40 } 41 attnmessage = question; 42 timeout = 0; 43 alarmcatch(); 44 for(;;){ 45 if ( fgets(replybuffer, 63, mytty) == NULL){ 46 if (ferror(mytty)){ 47 clearerr(mytty); 48 continue; 49 } 50 } else if ( (strcmp(replybuffer, "yes\n") == 0) || 51 (strcmp(replybuffer, "Yes\n") == 0)){ 52 back = 1; 53 goto done; 54 } else if ( (strcmp(replybuffer, "no\n") == 0) || 55 (strcmp(replybuffer, "No\n") == 0)){ 56 back = 0; 57 goto done; 58 } else { 59 msg("\"Yes\" or \"No\" ONLY!\n"); 60 alarmcatch(); 61 } 62 } 63 done: 64 /* 65 * Turn off the alarm, and reset the signal to trap out.. 66 */ 67 alarm(0); 68 if (signal(SIGALRM, sigalrm) == SIG_IGN) 69 signal(SIGALRM, SIG_IGN); 70 fclose(mytty); 71 return(back); 72 } 73 /* 74 * Alert the console operator, and enable the alarm clock to 75 * sleep for 2 minutes in case nobody comes to satisfy dump 76 */ 77 alarmcatch() 78 { 79 if (timeout) 80 msgtail("\n"); 81 msg("NEEDS ATTENTION: %s: (\"yes\" or \"no\") ", 82 attnmessage); 83 signal(SIGALRM, alarmcatch); 84 alarm(120); 85 timeout = 1; 86 } 87 /* 88 * Here if an inquisitive operator interrupts the dump program 89 */ 90 interrupt() 91 { 92 msg("Interrupt received. Do >>>YOU<<< know what are you doing?\n"); 93 if (query("Do you really want to abort dump?")) 94 dumpabort(); 95 signal(SIGINT, interrupt); 96 } 97 98 /* 99 * The following variables and routines manage alerting 100 * operators to the status of dump. 101 * This works much like wall(1) does. 102 */ 103 struct Group *gp; 104 105 /* 106 * Get the names from the group entry "operator" to notify. 107 */ 108 set_operators() 109 { 110 if (!notify) /*not going to notify*/ 111 return; 112 gp = getgrnam(OPGRENT); 113 endgrent(); 114 if (gp == (struct Group *)0){ 115 msg("No entry in /etc/group for %s.\n", 116 OPGRENT); 117 notify = 0; 118 return; 119 } 120 } 121 122 struct tm *localtime(); 123 struct tm *localclock; 124 125 /* 126 * We fork a child to do the actual broadcasting, so 127 * that the process control groups are not messed up 128 */ 129 broadcast(message) 130 char *message; 131 { 132 time_t clock; 133 FILE *f_utmp; 134 struct utmp utmp; 135 int nusers; 136 char **np; 137 int pid, s; 138 139 switch (pid = fork()) { 140 case -1: 141 return; 142 case 0: 143 break; 144 default: 145 while (wait(&s) != pid) 146 continue; 147 return; 148 } 149 150 if (!notify || gp == 0) 151 exit(0); 152 clock = time(0); 153 localclock = localtime(&clock); 154 155 if((f_utmp = fopen("/etc/utmp", "r")) == NULL) { 156 msg("Cannot open /etc/utmp\n"); 157 return; 158 } 159 160 nusers = 0; 161 while (!feof(f_utmp)){ 162 if (fread(&utmp, sizeof (struct utmp), 1, f_utmp) != 1) 163 break; 164 if (utmp.ut_name[0] == 0) 165 continue; 166 nusers++; 167 for (np = gp->gr_mem; *np; np++){ 168 if (strncmp(*np, utmp.ut_name, sizeof(utmp.ut_name)) != 0) 169 continue; 170 /* 171 * Do not send messages to operators on dialups 172 */ 173 if (strncmp(utmp.ut_line, DIALUP, strlen(DIALUP)) == 0) 174 continue; 175 #ifdef DEBUG 176 msg("Message to %s at %s\n", 177 utmp.ut_name, utmp.ut_line); 178 #endif DEBUG 179 sendmes(utmp.ut_line, message); 180 } 181 } 182 fclose(f_utmp); 183 Exit(0); /* the wait in this same routine will catch this */ 184 /* NOTREACHED */ 185 } 186 187 sendmes(tty, message) 188 char *tty, *message; 189 { 190 char t[50], buf[BUFSIZ]; 191 register char *cp; 192 register int c, ch; 193 int msize; 194 FILE *f_tty; 195 196 msize = strlen(message); 197 strcpy(t, "/dev/"); 198 strcat(t, tty); 199 200 if((f_tty = fopen(t, "w")) != NULL) { 201 setbuf(f_tty, buf); 202 fprintf(f_tty, "\nMessage from the dump program to all operators at %d:%02d ...\r\n\n" 203 ,localclock->tm_hour 204 ,localclock->tm_min); 205 for (cp = message, c = msize; c-- > 0; cp++) { 206 ch = *cp; 207 if (ch == '\n') 208 putc('\r', f_tty); 209 putc(ch, f_tty); 210 } 211 fclose(f_tty); 212 } 213 } 214 215 /* 216 * print out an estimate of the amount of time left to do the dump 217 */ 218 219 time_t tschedule = 0; 220 221 timeest() 222 { 223 time_t tnow, deltat; 224 225 time (&tnow); 226 if (tnow >= tschedule){ 227 tschedule = tnow + 300; 228 if (blockswritten < 500) 229 return; 230 deltat = tstart_writing - tnow + 231 (((1.0*(tnow - tstart_writing))/blockswritten) * esize); 232 msg("%3.2f%% done, finished in %d:%02d\n", 233 (blockswritten*100.0)/esize, 234 deltat/3600, (deltat%3600)/60); 235 } 236 } 237 238 int blocksontape() 239 { 240 /* 241 * esize: total number of blocks estimated over all reels 242 * blockswritten: blocks actually written, over all reels 243 * etapes: estimated number of tapes to write 244 * 245 * tsize: blocks can write on this reel 246 * asize: blocks written on this reel 247 * tapeno: number of tapes written so far 248 */ 249 if (tapeno == etapes) 250 return(esize - (etapes - 1)*tsize); 251 return(tsize); 252 } 253 254 /* VARARGS1 */ 255 /* ARGSUSED */ 256 msg(fmt, a1, a2, a3, a4, a5) 257 char *fmt; 258 int a1, a2, a3, a4, a5; 259 { 260 fprintf(stderr," DUMP: "); 261 #ifdef TDEBUG 262 fprintf(stderr,"pid=%d ", getpid()); 263 #endif 264 fprintf(stderr, fmt, a1, a2, a3, a4, a5); 265 fflush(stdout); 266 fflush(stderr); 267 } 268 269 /* VARARGS1 */ 270 /* ARGSUSED */ 271 msgtail(fmt, a1, a2, a3, a4, a5) 272 char *fmt; 273 int a1, a2, a3, a4, a5; 274 { 275 fprintf(stderr, fmt, a1, a2, a3, a4, a5); 276 } 277 /* 278 * Tell the operator what has to be done; 279 * we don't actually do it 280 */ 281 282 struct fstab * 283 allocfsent(fs) 284 register struct fstab *fs; 285 { 286 register struct fstab *new; 287 register char *cp; 288 char *malloc(); 289 290 new = (struct fstab *)malloc(sizeof (*fs)); 291 cp = malloc(strlen(fs->fs_file) + 1); 292 strcpy(cp, fs->fs_file); 293 new->fs_file = cp; 294 cp = malloc(strlen(fs->fs_type) + 1); 295 strcpy(cp, fs->fs_type); 296 new->fs_type = cp; 297 cp = malloc(strlen(fs->fs_spec) + 1); 298 strcpy(cp, fs->fs_spec); 299 new->fs_spec = cp; 300 new->fs_passno = fs->fs_passno; 301 new->fs_freq = fs->fs_freq; 302 return (new); 303 } 304 305 struct pfstab { 306 struct pfstab *pf_next; 307 struct fstab *pf_fstab; 308 }; 309 310 static struct pfstab *table = NULL; 311 312 getfstab() 313 { 314 register struct fstab *fs; 315 register struct pfstab *pf; 316 317 if (setfsent() == 0) { 318 msg("Can't open %s for dump table information.\n", FSTAB); 319 return; 320 } 321 while (fs = getfsent()) { 322 if (strcmp(fs->fs_type, FSTAB_RW) && 323 strcmp(fs->fs_type, FSTAB_RO) && 324 strcmp(fs->fs_type, FSTAB_RQ)) 325 continue; 326 fs = allocfsent(fs); 327 pf = (struct pfstab *)malloc(sizeof (*pf)); 328 pf->pf_fstab = fs; 329 pf->pf_next = table; 330 table = pf; 331 } 332 endfsent(); 333 } 334 335 /* 336 * Search in the fstab for a file name. 337 * This file name can be either the special or the path file name. 338 * 339 * The entries in the fstab are the BLOCK special names, not the 340 * character special names. 341 * The caller of fstabsearch assures that the character device 342 * is dumped (that is much faster) 343 * 344 * The file name can omit the leading '/'. 345 */ 346 struct fstab * 347 fstabsearch(key) 348 char *key; 349 { 350 register struct pfstab *pf; 351 register struct fstab *fs; 352 char *rawname(); 353 354 if (table == NULL) 355 return ((struct fstab *)0); 356 for (pf = table; pf; pf = pf->pf_next) { 357 fs = pf->pf_fstab; 358 if (strcmp(fs->fs_file, key) == 0) 359 return (fs); 360 if (strcmp(fs->fs_spec, key) == 0) 361 return (fs); 362 if (strcmp(rawname(fs->fs_spec), key) == 0) 363 return (fs); 364 if (key[0] != '/'){ 365 if (*fs->fs_spec == '/' && 366 strcmp(fs->fs_spec + 1, key) == 0) 367 return (fs); 368 if (*fs->fs_file == '/' && 369 strcmp(fs->fs_file + 1, key) == 0) 370 return (fs); 371 } 372 } 373 return (0); 374 } 375 376 /* 377 * Tell the operator what to do 378 */ 379 lastdump(arg) 380 char arg; /* w ==> just what to do; W ==> most recent dumps */ 381 { 382 char *lastname; 383 char *date; 384 register int i; 385 time_t tnow; 386 register struct fstab *dt; 387 int dumpme; 388 register struct idates *itwalk; 389 390 int idatesort(); 391 392 time(&tnow); 393 getfstab(); /* /etc/fstab input */ 394 inititimes(); /* /etc/dumpdates input */ 395 qsort(idatev, nidates, sizeof(struct idates *), idatesort); 396 397 if (arg == 'w') 398 fprintf(stdout, "Dump these file systems:\n"); 399 else 400 fprintf(stdout, "Last dump(s) done (Dump '>' file systems):\n"); 401 lastname = "??"; 402 ITITERATE(i, itwalk){ 403 if (strncmp(lastname, itwalk->id_name, sizeof(itwalk->id_name)) == 0) 404 continue; 405 date = (char *)ctime(&itwalk->id_ddate); 406 date[16] = '\0'; /* blast away seconds and year */ 407 lastname = itwalk->id_name; 408 dt = fstabsearch(itwalk->id_name); 409 dumpme = ( (dt != 0) 410 && (dt->fs_freq != 0) 411 && (itwalk->id_ddate < tnow - (dt->fs_freq*DAY))); 412 if ( (arg != 'w') || dumpme) 413 fprintf(stdout,"%c %8s\t(%6s) Last dump: Level %c, Date %s\n", 414 dumpme && (arg != 'w') ? '>' : ' ', 415 itwalk->id_name, 416 dt ? dt->fs_file : "", 417 itwalk->id_incno, 418 date 419 ); 420 } 421 } 422 423 int idatesort(p1, p2) 424 struct idates **p1, **p2; 425 { 426 int diff; 427 428 diff = strncmp((*p1)->id_name, (*p2)->id_name, sizeof((*p1)->id_name)); 429 if (diff == 0) 430 return ((*p2)->id_ddate - (*p1)->id_ddate); 431 else 432 return (diff); 433 } 434 435 int max(a,b) 436 int a, b; 437 { 438 return(a>b?a:b); 439 } 440 int min(a,b) 441 int a, b; 442 { 443 return(a<b?a:b); 444 } 445