File: | src/sbin/dump/tape.c |
Warning: | line 813, column 7 Although the value stored to 'nread' is used in the enclosing expression, the value is never actually read from 'nread' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: tape.c,v 1.48 2023/03/08 04:43:06 guenther Exp $ */ |
2 | /* $NetBSD: tape.c,v 1.11 1997/06/05 11:13:26 lukem Exp $ */ |
3 | |
4 | /*- |
5 | * Copyright (c) 1980, 1991, 1993 |
6 | * The Regents of the University of California. All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. Neither the name of the University nor the names of its contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 | * SUCH DAMAGE. |
31 | */ |
32 | |
33 | #include <sys/param.h> /* MAXBSIZE DEV_BSIZE */ |
34 | #include <sys/socket.h> |
35 | #include <sys/time.h> |
36 | #include <sys/wait.h> |
37 | #include <sys/stat.h> |
38 | #include <ufs/ffs/fs.h> |
39 | #include <ufs/ufs/dinode.h> |
40 | |
41 | #include <protocols/dumprestore.h> |
42 | |
43 | #include <errno(*__errno()).h> |
44 | #include <fcntl.h> |
45 | #include <signal.h> |
46 | #include <stdio.h> |
47 | #include <stdlib.h> |
48 | #include <string.h> |
49 | #include <time.h> |
50 | #include <unistd.h> |
51 | #include <limits.h> |
52 | |
53 | #include "dump.h" |
54 | #include "pathnames.h" |
55 | |
56 | #define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b)) |
57 | |
58 | ino_t curino; /* current inumber; used globally */ |
59 | int newtape; /* new tape flag */ |
60 | union u_spcl u_spcl; /* mapping of variables in a control block */ |
61 | |
62 | static int tapefd; /* tape file descriptor */ |
63 | static int64_t asize; /* number of 0.1" units written on cur tape */ |
64 | static int writesize; /* size of malloc()ed buffer for tape */ |
65 | static int64_t lastspclrec = -1; /* tape block number of last written header */ |
66 | static int trecno = 0; /* next record to write in current block */ |
67 | static int64_t blocksthisvol; /* number of blocks on current output file */ |
68 | static char *nexttape; |
69 | |
70 | static ssize_t atomic(ssize_t (*)(int, void *, size_t), int, char *, int); |
71 | static void doslave(int, int); |
72 | static void enslave(void); |
73 | static void flushtape(void); |
74 | static void killall(void); |
75 | static void rollforward(void); |
76 | |
77 | void tperror(int signo); |
78 | void sigpipe(int signo); |
79 | void proceed(int signo); |
80 | |
81 | /* |
82 | * Concurrent dump mods (Caltech) - disk block reading and tape writing |
83 | * are exported to several slave processes. While one slave writes the |
84 | * tape, the others read disk blocks; they pass control of the tape in |
85 | * a ring via signals. The parent process traverses the filesystem and |
86 | * sends writeheader()'s and lists of daddr's to the slaves via pipes. |
87 | * The following structure defines the instruction packets sent to slaves. |
88 | */ |
89 | struct req { |
90 | daddr_t dblk; |
91 | int count; |
92 | }; |
93 | static int reqsiz; |
94 | |
95 | #define SLAVES3 3 /* 1 slave writing, 1 reading, 1 for slack */ |
96 | static struct slave { |
97 | int64_t tapea; /* header number at start of this chunk */ |
98 | int64_t firstrec; /* record number of this block */ |
99 | int count; /* count to next header (used for TS_TAPE */ |
100 | /* after EOT) */ |
101 | int inode; /* inode that we are currently dealing with */ |
102 | int fd; /* FD for this slave */ |
103 | pid_t pid; /* PID for this slave */ |
104 | int sent; /* 1 == we've sent this slave requests */ |
105 | char (*tblock)[TP_BSIZE1024]; /* buffer for data blocks */ |
106 | struct req *req; /* buffer for requests */ |
107 | } slaves[SLAVES3+1]; |
108 | static struct slave *slp; |
109 | |
110 | static char (*nextblock)[TP_BSIZE1024]; |
111 | |
112 | static time_t tstart_volume; /* time of volume start */ |
113 | static int64_t tapea_volume; /* value of spcl.c_tapea at volume start */ |
114 | |
115 | static pid_t master; /* pid of master, for sending error signals */ |
116 | static int tenths; /* length of tape used per block written */ |
117 | static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ |
118 | |
119 | int |
120 | alloctape(void) |
121 | { |
122 | int pgoff = getpagesize() - 1; |
123 | char *buf; |
124 | int i; |
125 | |
126 | writesize = ntrec * TP_BSIZE1024; |
127 | reqsiz = (ntrec + 1) * sizeof(struct req); |
128 | /* |
129 | * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode |
130 | * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require |
131 | * repositioning after stopping, i.e, streaming mode, where the gap is |
132 | * variable, 0.30" to 0.45". The gap is maximal when the tape stops. |
133 | */ |
134 | if (blocksperfile == 0 && !unlimited) |
135 | tenths = writesize / density + |
136 | (cartridge ? 16 : density == 625 ? 5 : 8); |
137 | /* |
138 | * Allocate tape buffer contiguous with the array of instruction |
139 | * packets, so flushtape() can write them together with one write(). |
140 | * Align tape buffer on page boundary to speed up tape write(). |
141 | */ |
142 | for (i = 0; i <= SLAVES3; i++) { |
143 | buf = malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE1024)); |
144 | if (buf == NULL((void *)0)) |
145 | return(0); |
146 | slaves[i].tblock = (char (*)[TP_BSIZE1024]) |
147 | (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); |
148 | slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; |
149 | } |
150 | slp = &slaves[0]; |
151 | slp->count = 1; |
152 | slp->tapea = 0; |
153 | slp->firstrec = 0; |
154 | nextblock = slp->tblock; |
155 | return(1); |
156 | } |
157 | |
158 | void |
159 | writerec(char *dp, int isspcl) |
160 | { |
161 | |
162 | slp->req[trecno].dblk = 0; |
163 | slp->req[trecno].count = 1; |
164 | *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp; |
165 | if (isspcl) |
166 | lastspclrec = spclu_spcl.s_spcl.c_tapea; |
167 | trecno++; |
168 | spclu_spcl.s_spcl.c_tapea++; |
169 | if (trecno >= ntrec) |
170 | flushtape(); |
171 | } |
172 | |
173 | void |
174 | dumpblock(daddr_t blkno, int size) |
175 | { |
176 | int avail, tpblks; |
177 | daddr_t dblkno; |
178 | |
179 | dblkno = fsbtodb(sblock, blkno)((blkno) << (sblock)->fs_fsbtodb); |
180 | tpblks = size >> tp_bshift; |
181 | while ((avail = MINIMUM(tpblks, ntrec - trecno)(((tpblks) < (ntrec - trecno)) ? (tpblks) : (ntrec - trecno ))) > 0) { |
182 | slp->req[trecno].dblk = dblkno; |
183 | slp->req[trecno].count = avail; |
184 | trecno += avail; |
185 | spclu_spcl.s_spcl.c_tapea += avail; |
186 | if (trecno >= ntrec) |
187 | flushtape(); |
188 | dblkno += avail << (tp_bshift - (ffs(DEV_BSIZE(1 << 9)) - 1)); |
189 | tpblks -= avail; |
190 | } |
191 | } |
192 | |
193 | int nogripe = 0; |
194 | |
195 | void |
196 | tperror(int signo) |
197 | { |
198 | /* XXX - signal races */ |
199 | |
200 | if (pipeout) { |
201 | msg("write error on %s\n", tape); |
202 | quit("Cannot recover\n"); |
203 | /* NOTREACHED */ |
204 | } |
205 | msg("write error %lld blocks into volume %d\n", |
206 | (long long)blocksthisvol, tapeno); |
207 | broadcast("DUMP WRITE ERROR!\n"); |
208 | if (!query("Do you want to restart?")) |
209 | dumpabort(0); |
210 | msg("Closing this volume. Prepare to restart with new media;\n"); |
211 | msg("this dump volume will be rewritten.\n"); |
212 | killall(); |
213 | nogripe = 1; |
214 | close_rewind(); |
215 | Exit(X_REWRITE2); |
216 | } |
217 | |
218 | void |
219 | sigpipe(int signo) |
220 | { |
221 | |
222 | quit("Broken pipe\n"); |
223 | } |
224 | |
225 | /* |
226 | * do_stats -- |
227 | * Update xferrate stats |
228 | */ |
229 | time_t |
230 | do_stats(void) |
231 | { |
232 | time_t tnow, ttaken; |
233 | int64_t blocks; |
234 | |
235 | (void)time(&tnow); |
236 | ttaken = tnow - tstart_volume; |
237 | blocks = spclu_spcl.s_spcl.c_tapea - tapea_volume; |
238 | msg("Volume %d completed at: %s", tapeno, ctime(&tnow)); |
239 | if (ttaken > 0) { |
240 | msg("Volume %d took %lld:%02lld:%02lld\n", tapeno, |
241 | (long long)ttaken / 3600, ((long long)ttaken % 3600) / 60, |
242 | (long long)ttaken % 60); |
243 | blocks /= ttaken; |
244 | msg("Volume %d transfer rate: %lld KB/s\n", tapeno, blocks); |
245 | xferrate += blocks; |
246 | } |
247 | return(tnow); |
248 | } |
249 | |
250 | /* |
251 | * statussig -- |
252 | * information message upon receipt of SIGINFO |
253 | * (derived from optr.c::timeest()) |
254 | * XXX not safe |
255 | */ |
256 | void |
257 | statussig(int signo) |
258 | { |
259 | time_t tnow, deltat; |
260 | int save_errno = errno(*__errno()); |
261 | |
262 | if (blockswritten < 500) |
263 | return; |
264 | (void) time(&tnow); |
265 | deltat = tstart_writing - tnow + (1.0 * (tnow - tstart_writing)) |
266 | / blockswritten * tapesize; |
267 | /* XXX not safe due to floating point printf */ |
268 | dprintf(STDERR_FILENO2, |
269 | "dump: %s %3.2f%% done at %lld KB/s, finished in %d:%02d\n", |
270 | tape, (blockswritten * 100.0) / tapesize, |
271 | (spclu_spcl.s_spcl.c_tapea - tapea_volume) / (tnow - tstart_volume), |
272 | (int)(deltat / 3600), (int)((deltat % 3600) / 60)); |
273 | errno(*__errno()) = save_errno; |
274 | } |
275 | |
276 | static void |
277 | flushtape(void) |
278 | { |
279 | int i, blks, got; |
280 | int64_t lastfirstrec; |
281 | |
282 | int siz = (char *)nextblock - (char *)slp->req; |
283 | |
284 | slp->req[trecno].count = 0; /* Sentinel */ |
285 | |
286 | if (atomic((ssize_t (*)(int, void *, size_t))write, slp->fd, |
287 | (char *)slp->req, siz) != siz) |
288 | quit("error writing command pipe: %s\n", strerror(errno(*__errno()))); |
289 | slp->sent = 1; /* we sent a request, read the response later */ |
290 | |
291 | lastfirstrec = slp->firstrec; |
292 | |
293 | if (++slp >= &slaves[SLAVES3]) |
294 | slp = &slaves[0]; |
295 | |
296 | /* Read results back from next slave */ |
297 | if (slp->sent) { |
298 | if (atomic(read, slp->fd, (char *)&got, sizeof(got)) |
299 | != sizeof(got)) { |
300 | perror(" DUMP: error reading command pipe in master"); |
301 | dumpabort(0); |
302 | } |
303 | slp->sent = 0; |
304 | |
305 | /* Check for end of tape */ |
306 | if (got < writesize) { |
307 | msg("End of tape detected\n"); |
308 | |
309 | /* |
310 | * Drain the results, don't care what the values were. |
311 | * If we read them here then trewind won't... |
312 | */ |
313 | for (i = 0; i < SLAVES3; i++) { |
314 | if (slaves[i].sent) { |
315 | if (atomic(read, slaves[i].fd, |
316 | (char *)&got, sizeof(got)) |
317 | != sizeof(got)) { |
318 | perror(" DUMP: error reading command pipe in master"); |
319 | dumpabort(0); |
320 | } |
321 | slaves[i].sent = 0; |
322 | } |
323 | } |
324 | |
325 | close_rewind(); |
326 | rollforward(); |
327 | return; |
328 | } |
329 | } |
330 | |
331 | blks = 0; |
332 | if (spclu_spcl.s_spcl.c_type != TS_END5 && spclu_spcl.s_spcl.c_type != TS_CLRI6 && |
333 | spclu_spcl.s_spcl.c_type != TS_BITS3) { |
334 | if (spclu_spcl.s_spcl.c_count > TP_NINDIR(1024/2)) |
335 | quit("c_count too large\n"); |
336 | for (i = 0; i < spclu_spcl.s_spcl.c_count; i++) |
337 | if (spclu_spcl.s_spcl.c_addr[i] != 0) |
338 | blks++; |
339 | } |
340 | slp->count = lastspclrec + blks + 1 - spclu_spcl.s_spcl.c_tapea; |
341 | slp->tapea = spclu_spcl.s_spcl.c_tapea; |
342 | slp->firstrec = lastfirstrec + ntrec; |
343 | slp->inode = curino; |
344 | nextblock = slp->tblock; |
345 | trecno = 0; |
346 | asize += tenths; |
347 | blockswritten += ntrec; |
348 | blocksthisvol += ntrec; |
349 | if (!pipeout && !unlimited && (blocksperfile ? |
350 | (blocksthisvol >= blocksperfile) : (asize > tsize))) { |
351 | close_rewind(); |
352 | startnewtape(0); |
353 | } |
354 | timeest(); |
355 | } |
356 | |
357 | void |
358 | trewind(void) |
359 | { |
360 | struct stat sb; |
361 | int f, got; |
362 | |
363 | for (f = 0; f < SLAVES3; f++) { |
364 | /* |
365 | * Drain the results, but unlike EOT we DO (or should) care |
366 | * what the return values were, since if we detect EOT after |
367 | * we think we've written the last blocks to the tape anyway, |
368 | * we have to replay those blocks with rollforward. |
369 | * |
370 | * fixme: punt for now. |
371 | */ |
372 | if (slaves[f].sent) { |
373 | if (atomic(read, slaves[f].fd, (char *)&got, sizeof(got)) |
374 | != sizeof(got)) { |
375 | perror(" DUMP: error reading command pipe in master"); |
376 | dumpabort(0); |
377 | } |
378 | slaves[f].sent = 0; |
379 | if (got != writesize) { |
380 | msg("EOT detected in last 2 tape records!\n"); |
381 | msg("Use a longer tape, decrease the size estimate\n"); |
382 | quit("or use no size estimate at all.\n"); |
383 | } |
384 | } |
385 | (void) close(slaves[f].fd); |
386 | } |
387 | while (wait((int *)NULL((void *)0)) >= 0) /* wait for any signals from slaves */ |
388 | /* void */; |
389 | |
390 | if (pipeout) |
391 | return; |
392 | |
393 | msg("Closing %s\n", tape); |
394 | |
395 | #ifdef RDUMP1 |
396 | if (host) { |
397 | rmtclose(); |
398 | while (rmtopen(tape, O_RDONLY0x0000) < 0) |
399 | sleep(10); |
400 | rmtclose(); |
401 | return; |
402 | } |
403 | #endif |
404 | /* |
405 | * st(4) says: "Bit 1 of the minor number specifies whether an eject is |
406 | * attempted when the device is closed. When it is set, the device |
407 | * will attempt to eject its media on close ...". |
408 | * |
409 | * If the tape has been ejected, looping on open() will generate 'Media |
410 | * not present' errors until a tape is loaded. Once loaded the tape |
411 | * will be immediately ejected as a result of the second close(). |
412 | * |
413 | * So if the tape will be ejected, just close and return. |
414 | */ |
415 | if ((fstat(tapefd, &sb) == 0) && (minor(sb.st_rdev)((unsigned)((sb.st_rdev) & 0xff) | (((sb.st_rdev) & 0xffff0000 ) >> 8)) & 0x02)) { |
416 | (void) close(tapefd); |
417 | return; |
418 | } |
419 | |
420 | (void) close(tapefd); |
421 | while ((f = open(tape, O_RDONLY0x0000)) == -1) |
422 | sleep (10); |
423 | (void) close(f); |
424 | } |
425 | |
426 | void |
427 | close_rewind(void) |
428 | { |
429 | trewind(); |
430 | (void)do_stats(); |
431 | if (nexttape) |
432 | return; |
433 | if (!nogripe) { |
434 | msg("Change Volumes: Mount volume #%d\n", tapeno+1); |
435 | broadcast("CHANGE DUMP VOLUMES!\7\7\n"); |
436 | } |
437 | while (!query("Is the new volume mounted and ready to go?")) |
438 | if (query("Do you want to abort?")) { |
439 | dumpabort(0); |
440 | /*NOTREACHED*/ |
441 | } |
442 | } |
443 | |
444 | void |
445 | rollforward(void) |
446 | { |
447 | struct req *p, *q, *prev; |
448 | struct slave *tslp; |
449 | int i, size, got; |
450 | int64_t savedtapea; |
451 | union u_spcl *ntb, *otb; |
452 | tslp = &slaves[SLAVES3]; |
453 | ntb = (union u_spcl *)tslp->tblock[1]; |
454 | |
455 | /* |
456 | * Each of the N slaves should have requests that need to |
457 | * be replayed on the next tape. Use the extra slave buffers |
458 | * (slaves[SLAVES]) to construct request lists to be sent to |
459 | * each slave in turn. |
460 | */ |
461 | for (i = 0; i < SLAVES3; i++) { |
462 | q = &tslp->req[1]; |
463 | otb = (union u_spcl *)slp->tblock; |
464 | |
465 | /* |
466 | * For each request in the current slave, copy it to tslp. |
467 | */ |
468 | |
469 | prev = NULL((void *)0); |
470 | for (p = slp->req; p->count > 0; p += p->count) { |
471 | *q = *p; |
472 | if (p->dblk == 0) |
473 | *ntb++ = *otb++; /* copy the datablock also */ |
474 | prev = q; |
475 | q += q->count; |
476 | } |
477 | if (prev == NULL((void *)0)) |
478 | quit("rollforward: protocol botch\n"); |
479 | if (prev->dblk != 0) |
480 | prev->count -= 1; |
481 | else |
482 | ntb--; |
483 | q -= 1; |
484 | q->count = 0; |
485 | q = &tslp->req[0]; |
486 | if (i == 0) { |
487 | q->dblk = 0; |
488 | q->count = 1; |
489 | trecno = 0; |
490 | nextblock = tslp->tblock; |
491 | savedtapea = spclu_spcl.s_spcl.c_tapea; |
492 | spclu_spcl.s_spcl.c_tapea = slp->tapea; |
493 | startnewtape(0); |
494 | spclu_spcl.s_spcl.c_tapea = savedtapea; |
495 | lastspclrec = savedtapea - 1; |
496 | } |
497 | size = (char *)ntb - (char *)q; |
498 | if (atomic((ssize_t (*)(int, void *, size_t))write, |
499 | slp->fd, (char *)q, size) != size) { |
500 | perror(" DUMP: error writing command pipe"); |
501 | dumpabort(0); |
502 | } |
503 | slp->sent = 1; |
504 | if (++slp >= &slaves[SLAVES3]) |
505 | slp = &slaves[0]; |
506 | |
507 | q->count = 1; |
508 | |
509 | if (prev->dblk != 0) { |
510 | /* |
511 | * If the last one was a disk block, make the |
512 | * first of this one be the last bit of that disk |
513 | * block... |
514 | */ |
515 | q->dblk = prev->dblk + |
516 | prev->count * (TP_BSIZE1024 / DEV_BSIZE(1 << 9)); |
517 | ntb = (union u_spcl *)tslp->tblock; |
518 | } else { |
519 | /* |
520 | * It wasn't a disk block. Copy the data to its |
521 | * new location in the buffer. |
522 | */ |
523 | q->dblk = 0; |
524 | *((union u_spcl *)tslp->tblock) = *ntb; |
525 | ntb = (union u_spcl *)tslp->tblock[1]; |
526 | } |
527 | } |
528 | slp->req[0] = *q; |
529 | nextblock = slp->tblock; |
530 | if (q->dblk == 0) |
531 | nextblock++; |
532 | trecno = 1; |
533 | |
534 | /* |
535 | * Clear the first slaves' response. One hopes that it |
536 | * worked ok, otherwise the tape is much too short! |
537 | */ |
538 | if (slp->sent) { |
539 | if (atomic(read, slp->fd, (char *)&got, sizeof(got)) |
540 | != sizeof(got)) { |
541 | perror(" DUMP: error reading command pipe in master"); |
542 | dumpabort(0); |
543 | } |
544 | slp->sent = 0; |
545 | |
546 | if (got != writesize) { |
547 | quit("EOT detected at start of the tape!\n"); |
548 | } |
549 | } |
550 | } |
551 | |
552 | /* |
553 | * We implement taking and restoring checkpoints on the tape level. |
554 | * When each tape is opened, a new process is created by forking; this |
555 | * saves all of the necessary context in the parent. The child |
556 | * continues the dump; the parent waits around, saving the context. |
557 | * If the child returns X_REWRITE, then it had problems writing that tape; |
558 | * this causes the parent to fork again, duplicating the context, and |
559 | * everything continues as if nothing had happened. |
560 | */ |
561 | void |
562 | startnewtape(int top) |
563 | { |
564 | pid_t parentpid; |
565 | pid_t childpid; |
566 | int status; |
567 | pid_t waitingpid; |
568 | char *p; |
569 | sig_t interrupt_save; |
570 | |
571 | interrupt_save = signal(SIGINT2, SIG_IGN(void (*)(int))1); |
572 | parentpid = getpid(); |
573 | tapea_volume = spclu_spcl.s_spcl.c_tapea; |
574 | (void)time(&tstart_volume); |
575 | |
576 | restore_check_point: |
577 | (void)signal(SIGINT2, interrupt_save); |
578 | /* |
579 | * All signals are inherited... |
580 | */ |
581 | childpid = fork(); |
582 | if (childpid == -1) { |
583 | msg("Context save fork fails in parent %d\n", parentpid); |
584 | Exit(X_ABORT3); |
585 | } |
586 | if (childpid != 0) { |
587 | /* |
588 | * PARENT: |
589 | * save the context by waiting |
590 | * until the child doing all of the work returns. |
591 | * don't catch the interrupt |
592 | */ |
593 | signal(SIGINT2, SIG_IGN(void (*)(int))1); |
594 | #ifdef TDEBUG |
595 | msg("Tape: %d; parent process: %d child process %d\n", |
596 | tapeno+1, parentpid, childpid); |
597 | #endif /* TDEBUG */ |
598 | while ((waitingpid = wait(&status)) != childpid) |
599 | msg("Parent %d waiting for child %d has another child %d return\n", |
600 | parentpid, childpid, waitingpid); |
601 | if (status & 0xFF) { |
602 | msg("Child %d returns LOB status %o\n", |
603 | childpid, status&0xFF); |
604 | } |
605 | status = (status >> 8) & 0xFF; |
606 | #ifdef TDEBUG |
607 | switch(status) { |
608 | case X_FINOK0: |
609 | msg("Child %d finishes X_FINOK\n", childpid); |
610 | break; |
611 | case X_ABORT3: |
612 | msg("Child %d finishes X_ABORT\n", childpid); |
613 | break; |
614 | case X_REWRITE2: |
615 | msg("Child %d finishes X_REWRITE\n", childpid); |
616 | break; |
617 | default: |
618 | msg("Child %d finishes unknown %d\n", |
619 | childpid, status); |
620 | break; |
621 | } |
622 | #endif /* TDEBUG */ |
623 | switch(status) { |
624 | case X_FINOK0: |
625 | Exit(X_FINOK0); |
626 | break; |
627 | case X_ABORT3: |
628 | Exit(X_ABORT3); |
629 | break; |
630 | case X_REWRITE2: |
631 | goto restore_check_point; |
632 | default: |
633 | msg("Bad return code from dump: %d\n", status); |
634 | Exit(X_ABORT3); |
635 | } |
636 | /*NOTREACHED*/ |
637 | } else { /* we are the child; just continue */ |
638 | #ifdef TDEBUG |
639 | sleep(4); /* allow time for parent's message to get out */ |
640 | msg("Child on Tape %d has parent %d, my pid = %d\n", |
641 | tapeno+1, parentpid, getpid()); |
642 | #endif /* TDEBUG */ |
643 | /* |
644 | * If we have a name like "/dev/rst0,/dev/rst1", |
645 | * use the name before the comma first, and save |
646 | * the remaining names for subsequent volumes. |
647 | */ |
648 | tapeno++; /* current tape sequence */ |
649 | if (nexttape || strchr(tape, ',')) { |
650 | if (nexttape && *nexttape) |
651 | tape = nexttape; |
652 | if ((p = strchr(tape, ',')) != NULL((void *)0)) { |
653 | *p = '\0'; |
654 | nexttape = p + 1; |
655 | } else |
656 | nexttape = NULL((void *)0); |
657 | msg("Dumping volume %d on %s\n", tapeno, tape); |
658 | } |
659 | #ifdef RDUMP1 |
660 | while ((tapefd = (host ? rmtopen(tape, O_WRONLY0x0001|O_CREAT0x0200) : |
661 | pipeout ? 1 : open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1) |
662 | #else |
663 | while ((tapefd = (pipeout ? 1 : |
664 | open(tape, O_WRONLY0x0001|O_CREAT0x0200, 0666))) == -1) |
665 | #endif |
666 | { |
667 | msg("Cannot open output \"%s\".\n", tape); |
668 | if (!query("Do you want to retry the open?")) |
669 | dumpabort(0); |
670 | } |
671 | |
672 | enslave(); /* Share open tape file descriptor with slaves */ |
673 | |
674 | asize = 0; |
675 | blocksthisvol = 0; |
676 | if (top) |
677 | newtape++; /* new tape signal */ |
678 | spclu_spcl.s_spcl.c_count = slp->count; |
679 | /* |
680 | * measure firstrec in TP_BSIZE units since restore doesn't |
681 | * know the correct ntrec value... |
682 | */ |
683 | spclu_spcl.s_spcl.c_firstrec = slp->firstrec; |
684 | spclu_spcl.s_spcl.c_volume++; |
685 | spclu_spcl.s_spcl.c_type = TS_TAPE1; |
686 | if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119) |
687 | spclu_spcl.s_spcl.c_flags |= DR_NEWHEADER0x0001; |
688 | writeheader((ino_t)slp->inode); |
689 | if (sblock->fs_magic != FS_UFS2_MAGIC0x19540119) |
690 | spclu_spcl.s_spcl.c_flags &=~ DR_NEWHEADER0x0001; |
691 | msg("Volume %d started at: %s", tapeno, ctime(&tstart_volume)); |
692 | if (tapeno > 1) |
693 | msg("Volume %d begins with blocks from inode %llu\n", |
694 | tapeno, (unsigned long long)slp->inode); |
695 | } |
696 | } |
697 | |
698 | void |
699 | dumpabort(int signo) |
700 | { |
701 | |
702 | if (master != 0 && master != getpid()) |
703 | /* Signals master to call dumpabort */ |
704 | (void) kill(master, SIGTERM15); |
705 | else { |
706 | killall(); |
707 | msg("The ENTIRE dump is aborted.\n"); |
708 | } |
709 | #ifdef RDUMP1 |
710 | rmtclose(); |
711 | #endif |
712 | Exit(X_ABORT3); |
713 | } |
714 | |
715 | __dead__attribute__((__noreturn__)) void |
716 | Exit(int status) |
717 | { |
718 | |
719 | #ifdef TDEBUG |
720 | msg("pid = %d exits with status %d\n", getpid(), status); |
721 | #endif /* TDEBUG */ |
722 | exit(status); |
723 | } |
724 | |
725 | /* |
726 | * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. |
727 | */ |
728 | void |
729 | proceed(int signo) |
730 | { |
731 | caught++; |
732 | } |
733 | |
734 | void |
735 | enslave(void) |
736 | { |
737 | int cmd[2]; |
738 | int i, j; |
739 | |
740 | master = getpid(); |
741 | |
742 | signal(SIGTERM15, dumpabort); /* Slave sends SIGTERM on dumpabort() */ |
743 | signal(SIGPIPE13, sigpipe); |
744 | signal(SIGUSR130, tperror); /* Slave sends SIGUSR1 on tape errors */ |
745 | signal(SIGUSR231, proceed); /* Slave sends SIGUSR2 to next slave */ |
746 | |
747 | for (i = 0; i < SLAVES3; i++) { |
748 | if (i == slp - &slaves[0]) { |
749 | caught = 1; |
750 | } else { |
751 | caught = 0; |
752 | } |
753 | |
754 | if (socketpair(AF_UNIX1, SOCK_STREAM1, 0, cmd) == -1 || |
755 | (slaves[i].pid = fork()) == -1) |
756 | quit("too many slaves, %d (recompile smaller): %s\n", |
757 | i, strerror(errno(*__errno()))); |
758 | |
759 | slaves[i].fd = cmd[1]; |
760 | slaves[i].sent = 0; |
761 | if (slaves[i].pid == 0) { /* Slave starts up here */ |
762 | for (j = 0; j <= i; j++) |
763 | (void) close(slaves[j].fd); |
764 | signal(SIGINT2, SIG_IGN(void (*)(int))1); /* Master handles this */ |
765 | signal(SIGINFO29, SIG_IGN(void (*)(int))1); |
766 | doslave(cmd[0], i); |
767 | Exit(X_FINOK0); |
768 | } |
769 | } |
770 | |
771 | for (i = 0; i < SLAVES3; i++) |
772 | (void) atomic((ssize_t (*)(int, void *, size_t))write, |
773 | slaves[i].fd, (char *) &slaves[(i + 1) % SLAVES3].pid, |
774 | sizeof(slaves[0].pid)); |
775 | master = 0; |
776 | } |
777 | |
778 | void |
779 | killall(void) |
780 | { |
781 | int i; |
782 | |
783 | for (i = 0; i < SLAVES3; i++) |
784 | if (slaves[i].pid > 0) { |
785 | (void) kill(slaves[i].pid, SIGKILL9); |
786 | slaves[i].pid = 0; |
787 | } |
788 | } |
789 | |
790 | /* |
791 | * Synchronization - each process has a lockfile, and shares file |
792 | * descriptors to the following process's lockfile. When our write |
793 | * completes, we release our lock on the following process's lock- |
794 | * file, allowing the following process to lock it and proceed. We |
795 | * get the lock back for the next cycle by swapping descriptors. |
796 | */ |
797 | static void |
798 | doslave(int cmd, int slave_number) |
799 | { |
800 | int nread, nextslave, size, wrote = 0, eot_count; |
801 | sigset_t nsigset, osigset; |
802 | |
803 | /* |
804 | * Need our own seek pointer. |
805 | */ |
806 | (void) close(diskfd); |
807 | if ((diskfd = open(disk, O_RDONLY0x0000)) == -1) |
808 | quit("slave couldn't reopen disk: %s\n", strerror(errno(*__errno()))); |
809 | |
810 | /* |
811 | * Need the pid of the next slave in the loop... |
812 | */ |
813 | if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof(nextslave))) |
Although the value stored to 'nread' is used in the enclosing expression, the value is never actually read from 'nread' | |
814 | != sizeof(nextslave)) { |
815 | quit("master/slave protocol botched - didn't get pid of next slave.\n"); |
816 | } |
817 | |
818 | /* |
819 | * Get list of blocks to dump, read the blocks into tape buffer |
820 | */ |
821 | while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { |
822 | struct req *p = slp->req; |
823 | |
824 | for (trecno = 0; trecno < ntrec; |
825 | trecno += p->count, p += p->count) { |
826 | if (p->dblk) { |
827 | bread(p->dblk, slp->tblock[trecno], |
828 | p->count * TP_BSIZE1024); |
829 | } else { |
830 | if (p->count != 1 || atomic(read, cmd, |
831 | (char *)slp->tblock[trecno], |
832 | TP_BSIZE1024) != TP_BSIZE1024) |
833 | quit("master/slave protocol botched.\n"); |
834 | } |
835 | } |
836 | |
837 | sigemptyset(&nsigset); |
838 | sigaddset(&nsigset, SIGUSR231); |
839 | sigprocmask(SIG_BLOCK1, &nsigset, &osigset); |
840 | while (!caught) |
841 | sigsuspend(&osigset); |
842 | caught = 0; |
843 | sigprocmask(SIG_SETMASK3, &osigset, NULL((void *)0)); |
844 | |
845 | /* Try to write the data... */ |
846 | eot_count = 0; |
847 | size = 0; |
848 | |
849 | while (eot_count < 10 && size < writesize) { |
850 | #ifdef RDUMP1 |
851 | if (host) |
852 | wrote = rmtwrite(slp->tblock[0]+size, |
853 | writesize-size); |
854 | else |
855 | #endif |
856 | wrote = write(tapefd, slp->tblock[0]+size, |
857 | writesize-size); |
858 | #ifdef WRITEDEBUG |
859 | printf("slave %d wrote %d\n", slave_number, wrote); |
860 | #endif |
861 | if (wrote < 0) |
862 | break; |
863 | if (wrote == 0) |
864 | eot_count++; |
865 | size += wrote; |
866 | } |
867 | |
868 | #ifdef WRITEDEBUG |
869 | if (size != writesize) |
870 | printf("slave %d only wrote %d out of %d bytes and gave up.\n", |
871 | slave_number, size, writesize); |
872 | #endif |
873 | |
874 | if (eot_count > 0) |
875 | size = 0; |
876 | |
877 | /* |
878 | * Handle ENOSPC as an EOT condition |
879 | */ |
880 | if (wrote < 0 && errno(*__errno()) == ENOSPC28) { |
881 | wrote = 0; |
882 | eot_count++; |
883 | } |
884 | |
885 | if (size < 0) { |
886 | (void) kill(master, SIGUSR130); |
887 | sigemptyset(&nsigset); |
888 | for (;;) |
889 | sigsuspend(&nsigset); |
890 | } else { |
891 | /* |
892 | * pass size of write back to master |
893 | * (for EOT handling) |
894 | */ |
895 | (void) atomic((ssize_t (*)(int, void *, size_t))write, |
896 | cmd, (char *)&size, sizeof(size)); |
897 | } |
898 | |
899 | /* |
900 | * If partial write, don't want next slave to go. |
901 | * Also jolts him awake. |
902 | */ |
903 | (void) kill(nextslave, SIGUSR231); |
904 | } |
905 | if (nread != 0) |
906 | quit("error reading command pipe: %s\n", strerror(errno(*__errno()))); |
907 | } |
908 | |
909 | /* |
910 | * Since a read from a pipe may not return all we asked for, |
911 | * or a write may not write all we ask if we get a signal, |
912 | * loop until the count is satisfied (or error). |
913 | */ |
914 | static ssize_t |
915 | atomic(ssize_t (*func)(int, void *, size_t), int fd, char *buf, int count) |
916 | { |
917 | ssize_t got, need = count; |
918 | |
919 | while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) |
920 | buf += got; |
921 | return (got < 0 ? got : count - need); |
922 | } |