1 /* A program to put stress on a POSIX system (stress).
2 *
3 * Copyright (C) 2001, 2002 Amos Waterland <awaterl@yahoo.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <libgen.h>
23 #include <math.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <sys/wait.h>
31
32 /* By default, print all messages of severity info and above. */
33 static int global_debug = 2;
34
35 /* By default, just print warning for non-critical errors. */
36 static int global_ignore = 1;
37
38 /* By default, retry on non-critical errors every 50ms. */
39 static int global_retry = 50000;
40
41 /* By default, use this as backoff coefficient for good fork throughput. */
42 static int global_backoff = 3000;
43
44 /* By default, do not timeout. */
45 static int global_timeout = 0;
46
47 /* Name of this program */
48 static char *global_progname = PACKAGE;
49
50 /* By default, do not hang after allocating memory. */
51 static int global_vmhang = 0;
52
53 /* Implemention of runtime-selectable severity message printing. */
54 #define dbg if (global_debug >= 3) \
55 fprintf (stdout, "%s: debug: (%d) ", global_progname, __LINE__), \
56 fprintf
57 #define out if (global_debug >= 2) \
58 fprintf (stdout, "%s: info: ", global_progname), \
59 fprintf
60 #define wrn if (global_debug >= 1) \
61 fprintf (stderr, "%s: warn: (%d) ", global_progname, __LINE__), \
62 fprintf
63 #define err if (global_debug >= 0) \
64 fprintf (stderr, "%s: error: (%d) ", global_progname, __LINE__), \
65 fprintf
66
67 /* Implementation of check for option argument correctness. */
68 #define assert_arg(A) \
69 if (++i == argc || ((arg = argv[i])[0] == '-' && \
70 !isdigit ((int)arg[1]) )) \
71 { \
72 err (stderr, "missing argument to option '%s'\n", A); \
73 exit (1); \
74 }
75
76 /* Prototypes for utility functions. */
77 int usage(int status);
78 int version(int status);
79 long long atoll_s(const char *nptr);
80 long long atoll_b(const char *nptr);
81
82 /* Prototypes for the worker functions. */
83 int hogcpu(long long forks);
84 int hogio(long long forks);
85 int hogvm(long long forks, long long chunks, long long bytes);
86 int hoghdd(long long forks, int clean, long long files, long long bytes);
87
main(int argc,char ** argv)88 int main(int argc, char **argv)
89 {
90 int i, pid, children = 0, retval = 0;
91 long starttime, stoptime, runtime;
92
93 /* Variables that indicate which options have been selected. */
94 int do_dryrun = 0;
95 int do_timeout = 0;
96 int do_cpu = 0; /* Default to 1 fork. */
97 long long do_cpu_forks = 1;
98 int do_io = 0; /* Default to 1 fork. */
99 long long do_io_forks = 1;
100 int do_vm = 0; /* Default to 1 fork, 1 chunk of 256MB. */
101 long long do_vm_forks = 1;
102 long long do_vm_chunks = 1;
103 long long do_vm_bytes = 256 * 1024 * 1024;
104 int do_hdd = 0; /* Default to 1 fork, clean, 1 file of 1GB. */
105 long long do_hdd_forks = 1;
106 int do_hdd_clean = 0;
107 long long do_hdd_files = 1;
108 long long do_hdd_bytes = 1024 * 1024 * 1024;
109
110 /* Record our start time. */
111 if ((starttime = time(NULL)) == -1) {
112 err(stderr, "failed to acquire current time\n");
113 exit(1);
114 }
115
116 /* SuSv3 does not define any error conditions for this function. */
117 global_progname = basename(argv[0]);
118
119 /* For portability, parse command line options without getopt_long. */
120 for (i = 1; i < argc; i++) {
121 char *arg = argv[i];
122
123 if (strcmp(arg, "--help") == 0 || strcmp(arg, "-?") == 0) {
124 usage(0);
125 } else if (strcmp(arg, "--version") == 0) {
126 version(0);
127 } else if (strcmp(arg, "--verbose") == 0
128 || strcmp(arg, "-v") == 0) {
129 global_debug = 3;
130 } else if (strcmp(arg, "--quiet") == 0
131 || strcmp(arg, "-q") == 0) {
132 global_debug = 0;
133 } else if (strcmp(arg, "--dry-run") == 0
134 || strcmp(arg, "-n") == 0) {
135 do_dryrun = 1;
136 } else if (strcmp(arg, "--no-retry") == 0) {
137 global_ignore = 0;
138 dbg(stdout,
139 "turning off ignore of non-critical errors");
140 } else if (strcmp(arg, "--retry-delay") == 0) {
141 assert_arg("--retry-delay");
142 global_retry = atoll(arg);
143 dbg(stdout, "setting retry delay to %dus\n",
144 global_retry);
145 } else if (strcmp(arg, "--backoff") == 0) {
146 assert_arg("--backoff");
147 global_backoff = atoll(arg);
148 if (global_backoff < 0) {
149 err(stderr, "invalid backoff factor: %i\n",
150 global_backoff);
151 exit(1);
152 }
153 dbg(stdout, "setting backoff coeffient to %dus\n",
154 global_backoff);
155 } else if (strcmp(arg, "--timeout") == 0
156 || strcmp(arg, "-t") == 0) {
157 do_timeout = 1;
158 assert_arg("--timeout");
159 global_timeout = atoll_s(arg);
160 dbg(stdout, "setting timeout to %ds\n", global_timeout);
161 } else if (strcmp(arg, "--cpu") == 0 || strcmp(arg, "-c") == 0) {
162 do_cpu = 1;
163 assert_arg("--cpu");
164 do_cpu_forks = atoll_b(arg);
165 } else if (strcmp(arg, "--io") == 0 || strcmp(arg, "-i") == 0) {
166 do_io = 1;
167 assert_arg("--io");
168 do_io_forks = atoll_b(arg);
169 } else if (strcmp(arg, "--vm") == 0 || strcmp(arg, "-m") == 0) {
170 do_vm = 1;
171 assert_arg("--vm");
172 do_vm_forks = atoll_b(arg);
173 } else if (strcmp(arg, "--vm-chunks") == 0) {
174 assert_arg("--vm-chunks");
175 do_vm_chunks = atoll_b(arg);
176 } else if (strcmp(arg, "--vm-bytes") == 0) {
177 assert_arg("--vm-bytes");
178 do_vm_bytes = atoll_b(arg);
179 } else if (strcmp(arg, "--vm-hang") == 0) {
180 global_vmhang = 1;
181 } else if (strcmp(arg, "--hdd") == 0 || strcmp(arg, "-d") == 0) {
182 do_hdd = 1;
183 assert_arg("--hdd");
184 do_hdd_forks = atoll_b(arg);
185 } else if (strcmp(arg, "--hdd-noclean") == 0) {
186 do_hdd_clean = 2;
187 } else if (strcmp(arg, "--hdd-files") == 0) {
188 assert_arg("--hdd-files");
189 do_hdd_files = atoll_b(arg);
190 } else if (strcmp(arg, "--hdd-bytes") == 0) {
191 assert_arg("--hdd-bytes");
192 do_hdd_bytes = atoll_b(arg);
193 } else {
194 err(stderr, "unrecognized option: %s\n", arg);
195 exit(1);
196 }
197 }
198
199 /* Hog CPU option. */
200 if (do_cpu) {
201 out(stdout, "dispatching %lli hogcpu forks\n", do_cpu_forks);
202
203 switch (pid = fork()) {
204 case 0: /* child */
205 if (do_dryrun)
206 exit(0);
207 exit(hogcpu(do_cpu_forks));
208 case -1: /* error */
209 err(stderr, "hogcpu dispatcher fork failed\n");
210 exit(1);
211 default: /* parent */
212 children++;
213 dbg(stdout, "--> hogcpu dispatcher forked (%i)\n", pid);
214 }
215 }
216
217 /* Hog I/O option. */
218 if (do_io) {
219 out(stdout, "dispatching %lli hogio forks\n", do_io_forks);
220
221 switch (pid = fork()) {
222 case 0: /* child */
223 if (do_dryrun)
224 exit(0);
225 exit(hogio(do_io_forks));
226 case -1: /* error */
227 err(stderr, "hogio dispatcher fork failed\n");
228 exit(1);
229 default: /* parent */
230 children++;
231 dbg(stdout, "--> hogio dispatcher forked (%i)\n", pid);
232 }
233 }
234
235 /* Hog VM option. */
236 if (do_vm) {
237 out(stdout,
238 "dispatching %lli hogvm forks, each %lli chunks of %lli bytes\n",
239 do_vm_forks, do_vm_chunks, do_vm_bytes);
240
241 switch (pid = fork()) {
242 case 0: /* child */
243 if (do_dryrun)
244 exit(0);
245 exit(hogvm(do_vm_forks, do_vm_chunks, do_vm_bytes));
246 case -1: /* error */
247 err(stderr, "hogvm dispatcher fork failed\n");
248 exit(1);
249 default: /* parent */
250 children++;
251 dbg(stdout, "--> hogvm dispatcher forked (%i)\n", pid);
252 }
253 }
254
255 /* Hog HDD option. */
256 if (do_hdd) {
257 out(stdout, "dispatching %lli hoghdd forks, each %lli files of "
258 "%lli bytes\n", do_hdd_forks, do_hdd_files, do_hdd_bytes);
259
260 switch (pid = fork()) {
261 case 0: /* child */
262 if (do_dryrun)
263 exit(0);
264 exit(hoghdd
265 (do_hdd_forks, do_hdd_clean, do_hdd_files,
266 do_hdd_bytes));
267 case -1: /* error */
268 err(stderr, "hoghdd dispatcher fork failed\n");
269 exit(1);
270 default: /* parent */
271 children++;
272 dbg(stdout, "--> hoghdd dispatcher forked (%i)\n", pid);
273 }
274 }
275
276 /* We have no work to do, so bail out. */
277 if (children == 0)
278 usage(0);
279
280 /* Wait for our children to exit. */
281 while (children) {
282 int status, ret;
283
284 if ((pid = wait(&status)) > 0) {
285 if ((WIFEXITED(status)) != 0) {
286 if ((ret = WEXITSTATUS(status)) != 0) {
287 err(stderr,
288 "dispatcher %i returned error %i\n",
289 pid, ret);
290 retval += ret;
291 } else {
292 dbg(stdout,
293 "<-- dispatcher return (%i)\n",
294 pid);
295 }
296 } else {
297 err(stderr,
298 "dispatcher did not exit normally\n");
299 ++retval;
300 }
301
302 --children;
303 } else {
304 dbg(stdout, "wait() returned error: %s\n",
305 strerror(errno));
306 err(stderr, "detected missing dispatcher children\n");
307 ++retval;
308 break;
309 }
310 }
311
312 /* Record our stop time. */
313 if ((stoptime = time(NULL)) == -1) {
314 err(stderr, "failed to acquire current time\n");
315 exit(1);
316 }
317
318 /* Calculate our runtime. */
319 runtime = stoptime - starttime;
320
321 /* Print final status message. */
322 if (retval) {
323 err(stderr, "failed run completed in %lis\n", runtime);
324 } else {
325 out(stdout, "successful run completed in %lis\n", runtime);
326 }
327
328 exit(retval);
329 }
330
usage(int status)331 int usage(int status)
332 {
333 char *mesg =
334 "`%s' imposes certain types of compute stress on your system\n\n"
335 "Usage: %s [OPTION [ARG]] ...\n\n"
336 " -?, --help show this help statement\n"
337 " --version show version statement\n"
338 " -v, --verbose be verbose\n"
339 " -q, --quiet be quiet\n"
340 " -n, --dry-run show what would have been done\n"
341 " --no-retry exit rather than retry non-critical errors\n"
342 " --retry-delay n wait n us before continuing past error\n"
343 " -t, --timeout n timeout after n seconds\n"
344 " --backoff n wait for factor of n us before starting work\n"
345 " -c, --cpu n spawn n procs spinning on sqrt()\n"
346 " -i, --io n spawn n procs spinning on sync()\n"
347 " -m, --vm n spawn n procs spinning on malloc()\n"
348 " --vm-chunks c malloc c chunks (default is 1)\n"
349 " --vm-bytes b malloc chunks of b bytes (default is 256MB)\n"
350 " --vm-hang hang in a sleep loop after memory allocated\n"
351 " -d, --hdd n spawn n procs spinning on write()\n"
352 " --hdd-noclean do not unlink file to which random data written\n"
353 " --hdd-files f write to f files (default is 1)\n"
354 " --hdd-bytes b write b bytes (default is 1GB)\n\n"
355 "Infinity is denoted with 0. For -m, -d: n=0 means infinite redo,\n"
356 "n<0 means redo abs(n) times. Valid suffixes are m,h,d,y for time;\n"
357 "k,m,g for size.\n\n";
358
359 fprintf(stdout, mesg, global_progname, global_progname);
360
361 if (status <= 0)
362 exit(-1 * status);
363
364 return 0;
365 }
366
version(int status)367 int version(int status)
368 {
369 char *mesg = "%s %s\n";
370
371 fprintf(stdout, mesg, global_progname, VERSION);
372
373 if (status <= 0)
374 exit(-1 * status);
375
376 return 0;
377 }
378
379 /* Convert a string representation of a number with an optional size suffix
380 * to a long long.
381 */
atoll_b(const char * nptr)382 long long atoll_b(const char *nptr)
383 {
384 int pos;
385 char suffix;
386 long long factor = 1;
387
388 if ((pos = strlen(nptr) - 1) < 0) {
389 err(stderr, "invalid string\n");
390 exit(1);
391 }
392
393 switch (suffix = nptr[pos]) {
394 case 'k':
395 case 'K':
396 factor = 1024;
397 break;
398 case 'm':
399 case 'M':
400 factor = 1024 * 1024;
401 break;
402 case 'g':
403 case 'G':
404 factor = 1024 * 1024 * 1024;
405 break;
406 default:
407 if (suffix < '0' || suffix > '9') {
408 err(stderr, "unrecognized suffix: %c\n", suffix);
409 exit(1);
410 }
411 }
412
413 factor = atoll(nptr) * factor;
414
415 return factor;
416 }
417
418 /* Convert a string representation of a number with an optional time suffix
419 * to a long long.
420 */
atoll_s(const char * nptr)421 long long atoll_s(const char *nptr)
422 {
423 int pos;
424 char suffix;
425 long long factor = 1;
426
427 if ((pos = strlen(nptr) - 1) < 0) {
428 err(stderr, "invalid string\n");
429 exit(1);
430 }
431
432 switch (suffix = nptr[pos]) {
433 case 's':
434 case 'S':
435 factor = 1;
436 break;
437 case 'm':
438 case 'M':
439 factor = 60;
440 break;
441 case 'h':
442 case 'H':
443 factor = 60 * 60;
444 break;
445 case 'd':
446 case 'D':
447 factor = 60 * 60 * 24;
448 break;
449 case 'y':
450 case 'Y':
451 factor = 60 * 60 * 24 * 360;
452 break;
453 default:
454 if (suffix < '0' || suffix > '9') {
455 err(stderr, "unrecognized suffix: %c\n", suffix);
456 exit(1);
457 }
458 }
459
460 factor = atoll(nptr) * factor;
461
462 return factor;
463 }
464
hogcpu(long long forks)465 int hogcpu(long long forks)
466 {
467 long long i;
468 double d;
469 int pid, retval = 0;
470
471 /* Make local copies of global variables. */
472 int ignore = global_ignore;
473 int retry = global_retry;
474 int timeout = global_timeout;
475 long backoff = global_backoff * forks;
476
477 dbg(stdout, "using backoff sleep of %lius for hogcpu\n", backoff);
478
479 for (i = 0; forks == 0 || i < forks; i++) {
480 switch (pid = fork()) {
481 case 0: /* child */
482 alarm(timeout);
483
484 /* Use a backoff sleep to ensure we get good fork throughput. */
485 usleep(backoff);
486
487 while (1)
488 d = sqrt(rand());
489
490 /* This case never falls through; alarm signal can cause exit. */
491 case -1: /* error */
492 if (ignore) {
493 ++retval;
494 wrn(stderr,
495 "hogcpu worker fork failed, continuing\n");
496 usleep(retry);
497 continue;
498 }
499
500 err(stderr, "hogcpu worker fork failed\n");
501 return 1;
502 default: /* parent */
503 dbg(stdout, "--> hogcpu worker forked (%i)\n", pid);
504 }
505 }
506
507 /* Wait for our children to exit. */
508 while (i) {
509 int status, ret;
510
511 if ((pid = wait(&status)) > 0) {
512 if ((WIFEXITED(status)) != 0) {
513 if ((ret = WEXITSTATUS(status)) != 0) {
514 err(stderr,
515 "hogcpu worker %i exited %i\n", pid,
516 ret);
517 retval += ret;
518 } else {
519 dbg(stdout,
520 "<-- hogcpu worker exited (%i)\n",
521 pid);
522 }
523 } else {
524 dbg(stdout,
525 "<-- hogcpu worker signalled (%i)\n", pid);
526 }
527
528 --i;
529 } else {
530 dbg(stdout, "wait() returned error: %s\n",
531 strerror(errno));
532 err(stderr,
533 "detected missing hogcpu worker children\n");
534 ++retval;
535 break;
536 }
537 }
538
539 return retval;
540 }
541
hogio(long long forks)542 int hogio(long long forks)
543 {
544 long long i;
545 int pid, retval = 0;
546
547 /* Make local copies of global variables. */
548 int ignore = global_ignore;
549 int retry = global_retry;
550 int timeout = global_timeout;
551 long backoff = global_backoff * forks;
552
553 dbg(stdout, "using backoff sleep of %lius for hogio\n", backoff);
554
555 for (i = 0; forks == 0 || i < forks; i++) {
556 switch (pid = fork()) {
557 case 0: /* child */
558 alarm(timeout);
559
560 /* Use a backoff sleep to ensure we get good fork throughput. */
561 usleep(backoff);
562
563 while (1)
564 sync();
565
566 /* This case never falls through; alarm signal can cause exit. */
567 case -1: /* error */
568 if (ignore) {
569 ++retval;
570 wrn(stderr,
571 "hogio worker fork failed, continuing\n");
572 usleep(retry);
573 continue;
574 }
575
576 err(stderr, "hogio worker fork failed\n");
577 return 1;
578 default: /* parent */
579 dbg(stdout, "--> hogio worker forked (%i)\n", pid);
580 }
581 }
582
583 /* Wait for our children to exit. */
584 while (i) {
585 int status, ret;
586
587 if ((pid = wait(&status)) > 0) {
588 if ((WIFEXITED(status)) != 0) {
589 if ((ret = WEXITSTATUS(status)) != 0) {
590 err(stderr,
591 "hogio worker %i exited %i\n", pid,
592 ret);
593 retval += ret;
594 } else {
595 dbg(stdout,
596 "<-- hogio worker exited (%i)\n",
597 pid);
598 }
599 } else {
600 dbg(stdout, "<-- hogio worker signalled (%i)\n",
601 pid);
602 }
603
604 --i;
605 } else {
606 dbg(stdout, "wait() returned error: %s\n",
607 strerror(errno));
608 err(stderr, "detected missing hogio worker children\n");
609 ++retval;
610 break;
611 }
612 }
613
614 return retval;
615 }
616
hogvm(long long forks,long long chunks,long long bytes)617 int hogvm(long long forks, long long chunks, long long bytes)
618 {
619 long long i, j, k;
620 int pid, retval = 0;
621 char **ptr;
622
623 /* Make local copies of global variables. */
624 int ignore = global_ignore;
625 int retry = global_retry;
626 int timeout = global_timeout;
627 long backoff = global_backoff * forks;
628
629 dbg(stdout, "using backoff sleep of %lius for hogvm\n", backoff);
630
631 if (bytes == 0) {
632 /* 512MB is guess at the largest value can than be malloced at once. */
633 bytes = 512 * 1024 * 1024;
634 }
635
636 for (i = 0; forks == 0 || i < forks; i++) {
637 switch (pid = fork()) {
638 case 0: /* child */
639 alarm(timeout);
640
641 /* Use a backoff sleep to ensure we get good fork throughput. */
642 usleep(backoff);
643
644 while (1) {
645 ptr = (char **)malloc(chunks * 2);
646 for (j = 0; chunks == 0 || j < chunks; j++) {
647 if ((ptr[j] =
648 (char *)malloc(bytes *
649 sizeof(char)))) {
650 for (k = 0; k < bytes; k++)
651 ptr[j][k] = 'Z'; /* Ensure that COW happens. */
652 dbg(stdout,
653 "hogvm worker malloced %lli bytes\n",
654 k);
655 } else if (ignore) {
656 ++retval;
657 wrn(stderr,
658 "hogvm malloc failed, continuing\n");
659 usleep(retry);
660 continue;
661 } else {
662 ++retval;
663 err(stderr,
664 "hogvm malloc failed\n");
665 break;
666 }
667 }
668 if (global_vmhang && retval == 0) {
669 dbg(stdout,
670 "sleeping forever with allocated memory\n");
671 while (1)
672 sleep(1024);
673 }
674 if (retval == 0) {
675 dbg(stdout,
676 "hogvm worker freeing memory and starting over\n");
677 for (j = 0; chunks == 0 || j < chunks;
678 j++) {
679 free(ptr[j]);
680 }
681 free(ptr);
682 continue;
683 }
684
685 exit(retval);
686 }
687
688 /* This case never falls through; alarm signal can cause exit. */
689 case -1: /* error */
690 if (ignore) {
691 ++retval;
692 wrn(stderr,
693 "hogvm worker fork failed, continuing\n");
694 usleep(retry);
695 continue;
696 }
697
698 err(stderr, "hogvm worker fork failed\n");
699 return 1;
700 default: /* parent */
701 dbg(stdout, "--> hogvm worker forked (%i)\n", pid);
702 }
703 }
704
705 /* Wait for our children to exit. */
706 while (i) {
707 int status, ret;
708
709 if ((pid = wait(&status)) > 0) {
710 if ((WIFEXITED(status)) != 0) {
711 if ((ret = WEXITSTATUS(status)) != 0) {
712 err(stderr,
713 "hogvm worker %i exited %i\n", pid,
714 ret);
715 retval += ret;
716 } else {
717 dbg(stdout,
718 "<-- hogvm worker exited (%i)\n",
719 pid);
720 }
721 } else {
722 dbg(stdout, "<-- hogvm worker signalled (%i)\n",
723 pid);
724 }
725
726 --i;
727 } else {
728 dbg(stdout, "wait() returned error: %s\n",
729 strerror(errno));
730 err(stderr, "detected missing hogvm worker children\n");
731 ++retval;
732 break;
733 }
734 }
735
736 return retval;
737 }
738
hoghdd(long long forks,int clean,long long files,long long bytes)739 int hoghdd(long long forks, int clean, long long files, long long bytes)
740 {
741 long long i, j;
742 int fd, pid, retval = 0;
743 int chunk = (1024 * 1024) - 1; /* Minimize slow writing. */
744 char buff[chunk];
745
746 /* Make local copies of global variables. */
747 int ignore = global_ignore;
748 int retry = global_retry;
749 int timeout = global_timeout;
750 long backoff = global_backoff * forks;
751
752 /* Initialize buffer with some random ASCII data. */
753 dbg(stdout, "seeding buffer with random data\n");
754 for (i = 0; i < chunk - 1; i++) {
755 j = rand();
756 j = (j < 0) ? -j : j;
757 j %= 95;
758 j += 32;
759 buff[i] = j;
760 }
761 buff[i] = '\n';
762
763 dbg(stdout, "using backoff sleep of %lius for hoghdd\n", backoff);
764
765 for (i = 0; forks == 0 || i < forks; i++) {
766 switch (pid = fork()) {
767 case 0: /* child */
768 alarm(timeout);
769
770 /* Use a backoff sleep to ensure we get good fork throughput. */
771 usleep(backoff);
772
773 while (1) {
774 for (i = 0; i < files; i++) {
775 char name[] = "./stress.XXXXXX";
776
777 if ((fd = mkstemp(name)) < 0) {
778 perror("mkstemp");
779 err(stderr, "mkstemp failed\n");
780 exit(1);
781 }
782
783 if (clean == 0) {
784 dbg(stdout, "unlinking %s\n",
785 name);
786 if (unlink(name)) {
787 err(stderr,
788 "unlink failed\n");
789 exit(1);
790 }
791 }
792
793 dbg(stdout, "fast writing to %s\n",
794 name);
795 for (j = 0;
796 bytes == 0 || j + chunk < bytes;
797 j += chunk) {
798 if (write(fd, buff, chunk) !=
799 chunk) {
800 err(stderr,
801 "write failed\n");
802 exit(1);
803 }
804 }
805
806 dbg(stdout, "slow writing to %s\n",
807 name);
808 for (; bytes == 0 || j < bytes - 1; j++) {
809 if (write(fd, "Z", 1) != 1) {
810 err(stderr,
811 "write failed\n");
812 exit(1);
813 }
814 }
815 if (write(fd, "\n", 1) != 1) {
816 err(stderr, "write failed\n");
817 exit(1);
818 }
819 ++j;
820
821 dbg(stdout,
822 "closing %s after writing %lli bytes\n",
823 name, j);
824 close(fd);
825
826 if (clean == 1) {
827 if (unlink(name)) {
828 err(stderr,
829 "unlink failed\n");
830 exit(1);
831 }
832 }
833 }
834 if (retval == 0) {
835 dbg(stdout,
836 "hoghdd worker starting over\n");
837 continue;
838 }
839
840 exit(retval);
841 }
842
843 /* This case never falls through; alarm signal can cause exit. */
844 case -1: /* error */
845 if (ignore) {
846 ++retval;
847 wrn(stderr,
848 "hoghdd worker fork failed, continuing\n");
849 usleep(retry);
850 continue;
851 }
852
853 err(stderr, "hoghdd worker fork failed\n");
854 return 1;
855 default: /* parent */
856 dbg(stdout, "--> hoghdd worker forked (%i)\n", pid);
857 }
858 }
859
860 /* Wait for our children to exit. */
861 while (i) {
862 int status, ret;
863
864 if ((pid = wait(&status)) > 0) {
865 if ((WIFEXITED(status)) != 0) {
866 if ((ret = WEXITSTATUS(status)) != 0) {
867 err(stderr,
868 "hoghdd worker %i exited %i\n", pid,
869 ret);
870 retval += ret;
871 } else {
872 dbg(stdout,
873 "<-- hoghdd worker exited (%i)\n",
874 pid);
875 }
876 } else {
877 dbg(stdout,
878 "<-- hoghdd worker signalled (%i)\n", pid);
879 }
880
881 --i;
882 } else {
883 dbg(stdout, "wait() returned error: %s\n",
884 strerror(errno));
885 err(stderr,
886 "detected missing hoghdd worker children\n");
887 ++retval;
888 break;
889 }
890 }
891
892 return retval;
893 }
894