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