1 /*******************************************************************************
2  *  The BYTE UNIX Benchmarks - Release 3
3  *          Module: big.c   SID: 3.3 5/15/91 19:30:18
4  *
5  *******************************************************************************
6  * Bug reports, patches, comments, suggestions should be sent to:
7  *
8  *	Ben Smith, Rick Grehan or Tom Yager
9  *	ben@bytepb.byte.com   rick_g@bytepb.byte.com   tyager@bytepb.byte.com
10  *
11  *******************************************************************************
12  *  Modification Log:
13  *  10/22/97 - code cleanup to remove ANSI C compiler warnings
14  *             Andy Kahn <kahn@zk3.dec.com>
15  *
16  ******************************************************************************/
17 /*
18  *  dummy code for execl test [ old version of makework.c ]
19  *
20  *  makework [ -r rate ] [ -c copyfile ] nusers
21  *
22  *  job streams are specified on standard input with lines of the form
23  *  full_path_name_for_command [ options ] [ <standard_input_file ]
24  *
25  *  "standard input" is send to all nuser instances of the commands in the
26  *  job streams at a rate not in excess of "rate" characters per second
27  *  per command
28  *
29  */
30 /* this code is included in other files and therefore has no SCCSid */
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <sys/types.h>
35 #include <signal.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <string.h>
39 #include <sys/wait.h>
40 
41 
42 #define DEF_RATE	5.0
43 #define GRANULE		5
44 #define CHUNK		60
45 #define MAXCHILD	12
46 #define MAXWORK		10
47 
48 void	wrapup(const char *);
49 void	onalarm(int);
50 void	pipeerr();
51 void	grunt();
52 void getwork(void);
53 #if debug
54 void dumpwork(void);
55 #endif
56 void fatal(const char *s);
57 
58 float	thres;
59 float	est_rate = DEF_RATE;
60 int	nusers;		/* number of concurrent users to be simulated by
61 			 * this process */
62 int	firstuser;	/* ordinal identification of first user for this
63 			 * process */
64 int	nwork = 0;	/* number of job streams */
65 int	exit_status = 0;	/* returned to parent */
66 int	sigpipe;	/* pipe write error flag */
67 
68 struct st_work {
69 	char	*cmd;		/* name of command to run */
70 	char	**av;		/* arguments to command */
71 	char	*input;		/* standard input buffer */
72 	int	inpsize;	/* size of standard input buffer */
73 	char	*outf;		/* standard output (filename) */
74 } work[MAXWORK];
75 
76 struct {
77 	int	xmit;	/* # characters sent */
78 	char	*bp;	/* std input buffer pointer */
79 	int	blen;	/* std input buffer length */
80 	int	fd;	/* stdin to command */
81 	int	pid;	/* child PID */
82 	char	*line;	/* start of input line */
83 	int	firstjob;	/* inital piece of work */
84 	int	thisjob;	/* current piece of work */
85 } child[MAXCHILD], *cp;
86 
main(argc,argv)87 int main(argc, argv)
88 int	argc;
89 char	*argv[];
90 {
91     int		i;
92     int		l;
93     int		fcopy = 0;	/* fd for copy output */
94     int		master = 1;	/* the REAL master, == 0 for clones */
95     int		nchild;		/* no. of children for a clone to run */
96     int		done;		/* count of children finished */
97     int		output;		/* aggregate output char count for all
98 				   children */
99     int		c;
100     int		thiswork = 0;	/* next job stream to allocate */
101     int		nch;		/* # characters to write */
102     int		written;	/* # characters actully written */
103     char	logname[32];	/* name of the log file(s) */
104     int		pvec[2];	/* for pipes */
105     char	*p;
106     char	*prog;		/* my name */
107 
108 #if ! debug
109     freopen("masterlog.00", "a", stderr);
110 #endif
111     prog = argv[0];
112     while (argc > 1 && argv[1][0] == '-')  {
113 	p = &argv[1][1];
114 	argc--;
115 	argv++;
116 	while (*p) {
117 	    switch (*p) {
118 	    case 'r':
119 			est_rate = atoi(argv[1]);
120 			sscanf(argv[1], "%f", &est_rate);
121 			if (est_rate <= 0) {
122 			    fprintf(stderr, "%s: bad rate, reset to %.2f chars/sec\n", prog, DEF_RATE);
123 			    est_rate = DEF_RATE;
124 			}
125 			argc--;
126 			argv++;
127 			break;
128 
129 	    case 'c':
130 			fcopy = open(argv[1], 1);
131 			if (fcopy < 0)
132 				fcopy = creat(argv[1], 0600);
133 			if (fcopy < 0) {
134 			    fprintf(stderr, "%s: cannot open copy file '%s'\n",
135 				prog, argv[1]);
136 			    exit(2);
137 			}
138 			lseek(fcopy, 0L, 2);	/* append at end of file */
139 			argc--;
140 			argv++;
141 			break;
142 
143 	    default:
144 		fprintf(stderr, "%s: bad flag '%c'\n", prog, *p);
145 			exit(4);
146 	    }
147 	    p++;
148 	}
149     }
150 
151     if (argc < 2) {
152 	fprintf(stderr, "%s: missing nusers\n", prog);
153 	exit(4);
154     }
155 
156     nusers = atoi(argv[1]);
157     if (nusers < 1) {
158 	fprintf(stderr, "%s: impossible nusers (%d<-%s)\n", prog, nusers, argv[1]);
159 	exit(4);
160     }
161     fprintf(stderr, "%d Users\n", nusers);
162     argc--;
163     argv++;
164 
165     /* build job streams */
166     getwork();
167 #if debug
168     dumpwork();
169 #endif
170 
171     /* clone copies of myself to run up to MAXCHILD jobs each */
172     firstuser = MAXCHILD;
173     fprintf(stderr, "master pid %d\n", getpid());
174     fflush(stderr);
175     while (nusers > MAXCHILD) {
176 	fflush(stderr);
177 	if (nusers >= 2*MAXCHILD)
178 	    /* the next clone must run MAXCHILD jobs */
179 	    nchild = MAXCHILD;
180 	else
181 	    /* the next clone must run the leftover jobs */
182 	    nchild = nusers - MAXCHILD;
183 	if ((l = fork()) == -1) {
184 	    /* fork failed */
185 	    fatal("** clone fork failed **\n");
186 	    goto bepatient;
187 	} else if (l > 0) {
188 	    fprintf(stderr, "master clone pid %d\n", l);
189 	    /* I am the master with nchild fewer jobs to run */
190 	    nusers -= nchild;
191 	    firstuser += MAXCHILD;
192 	    continue;
193 	} else {
194 	    /* I am a clone, run MAXCHILD jobs */
195 #if ! debug
196 	    sprintf(logname, "masterlog.%02d", firstuser/MAXCHILD);
197 	    freopen(logname, "w", stderr);
198 #endif
199 	    master = 0;
200 	    nusers = nchild;
201 	    break;
202 	}
203     }
204     if (master)
205 	firstuser = 0;
206 
207     close(0);
208     for (i = 0; i < nusers; i++ ) {
209 	fprintf(stderr, "user %d job %d ", firstuser+i, thiswork);
210 	if (pipe(pvec) == -1) {
211 	    /* this is fatal */
212 	    fatal("** pipe failed **\n");
213 	    goto bepatient;
214 	}
215 	fflush(stderr);
216 	if ((child[i].pid = fork()) == 0) {
217 	    int	fd;
218 	    /* the command */
219 	    if (pvec[0] != 0) {
220 		close(0);
221 		dup(pvec[0]);
222 	    }
223 #if ! debug
224 	    sprintf(logname, "userlog.%02d", firstuser+i);
225 	    freopen(logname, "w", stderr);
226 #endif
227 	    for (fd = 3; fd < 24; fd++)
228 		close(fd);
229 	    if (work[thiswork].outf[0] != '\0') {
230 		/* redirect std output */
231 		char	*q;
232 		for (q = work[thiswork].outf; *q != '\n'; q++) ;
233 		*q = '\0';
234 		if (freopen(work[thiswork].outf, "w", stdout) == NULL) {
235 		    fprintf(stderr, "makework: cannot open %s for std output\n",
236 			work[thiswork].outf);
237 		    fflush(stderr);
238 		}
239 		*q = '\n';
240 	    }
241 	    execv(work[thiswork].cmd, work[thiswork].av);
242 	    /* don't expect to get here! */
243 	    fatal("** exec failed **\n");
244 	    goto bepatient;
245 	}
246 	else if (child[i].pid == -1) {
247 	    fatal("** fork failed **\n");
248 	    goto bepatient;
249 	}
250 	else {
251 	    close(pvec[0]);
252 	    child[i].fd = pvec[1];
253 	    child[i].line = child[i].bp = work[thiswork].input;
254 	    child[i].blen = work[thiswork].inpsize;
255 	    child[i].thisjob = thiswork;
256 	    child[i].firstjob = thiswork;
257 	    fprintf(stderr, "pid %d pipe fd %d", child[i].pid, child[i].fd);
258 	    if (work[thiswork].outf[0] != '\0') {
259 		char *q;
260 		fprintf(stderr, " > ");
261 		for (q=work[thiswork].outf; *q != '\n'; q++)
262 		    fputc(*q, stderr);
263 	    }
264 	    fputc('\n', stderr);
265 	    thiswork++;
266 	    if (thiswork >= nwork)
267 		thiswork = 0;
268 	}
269     }
270     fflush(stderr);
271 
272     srand(time(0));
273     thres = 0;
274     done = output = 0;
275     for (i = 0; i < nusers; i++) {
276 	if (child[i].blen == 0)
277 	    done++;
278 	else
279 	    thres += est_rate * GRANULE;
280     }
281     est_rate = thres;
282 
283     signal(SIGALRM, onalarm);
284     signal(SIGPIPE, pipeerr);
285     alarm(GRANULE);
286     while (done < nusers) {
287 	for (i = 0; i < nusers; i++) {
288 	    cp = &child[i];
289 	    if (cp->xmit >= cp->blen) continue;
290 	    l = rand() % CHUNK + 1;	/* 1-CHUNK chars */
291 	    if (l == 0) continue;
292 	    if (cp->xmit + l > cp->blen)
293 		l = cp->blen - cp->xmit;
294 	    p = cp->bp;
295 	    cp->bp += l;
296 	    cp->xmit += l;
297 #if debug
298 	    fprintf(stderr, "child %d, %d processed, %d to go\n", i, cp->xmit, cp->blen - cp->xmit);
299 #endif
300 	    while (p < cp->bp) {
301 		if (*p == '\n' || (p == &cp->bp[-1] && cp->xmit >= cp->blen)) {
302 		    /* write it out */
303 		    nch = p - cp->line + 1;
304 		    if ((written = write(cp->fd, cp->line, nch)) != nch) {
305 			/* argh! */
306 			cp->line[nch] = '\0';
307 			fprintf(stderr, "user %d job %d cmd %s ",
308 				firstuser+i, cp->thisjob, cp->line);
309  			fprintf(stderr, "write(,,%d) returns %d\n", nch, written);
310 			if (sigpipe)
311 			    fatal("** SIGPIPE error **\n");
312 			else
313 			    fatal("** write error **\n");
314 			goto bepatient;
315 
316 		    }
317 		    if (fcopy)
318 			write(fcopy, cp->line, p - cp->line + 1);
319 #if debug
320 		    fprintf(stderr, "child %d gets \"", i);
321 		    {
322 			char *q = cp->line;
323 			while (q <= p) {
324 				if (*q >= ' ' && *q <= '~')
325 					fputc(*q, stderr);
326 				else
327 					fprintf(stderr, "\\%03o", *q);
328 				q++;
329 			}
330 		    }
331 		    fputc('"', stderr);
332 #endif
333 		    cp->line = &p[1];
334 		}
335 		p++;
336 	    }
337 	    if (cp->xmit >= cp->blen) {
338 		done++;
339 		close(cp->fd);
340 #if debug
341 	fprintf(stderr, "child %d, close std input\n", i);
342 #endif
343 	    }
344 	    output += l;
345 	}
346 	while (output > thres) {
347 	    pause();
348 #if debug
349 	    fprintf(stderr, "after pause: output, thres, done %d %.2f %d\n", output, thres, done);
350 #endif
351 	}
352     }
353 
354 bepatient:
355     alarm(0);
356 /****
357  *  If everything is going OK, we should simply be able to keep
358  *  looping unitil 'wait' fails, however some descendent process may
359  *  be in a state from which it can never exit, and so a timeout
360  *  is used.
361  *  5 minutes should be ample, since the time to run all jobs is of
362  *  the order of 5-10 minutes, however some machines are painfully slow,
363  *  so the timeout has been set at 20 minutes (1200 seconds).
364  ****/
365     signal(SIGALRM, grunt);
366     alarm(1200);
367     while ((c = wait(&l)) != -1) {
368         for (i = 0; i < nusers; i++) {
369 	    if (c == child[i].pid) {
370 		fprintf(stderr, "user %d job %d pid %d done", firstuser+i, child[i].thisjob, c);
371 		if (l != 0) {
372 		    if (l & 0x7f)
373 			fprintf(stderr, " status %d", l & 0x7f);
374 		    if (l & 0xff00)
375 			fprintf(stderr, " exit code %d", (l>>8) & 0xff);
376 		    exit_status = 4;
377 		}
378 		fputc('\n', stderr);
379 		c = child[i].pid = -1;
380 		break;
381 	    }
382 	}
383 	if (c != -1) {
384 	    fprintf(stderr, "master clone done, pid %d ", c);
385 	    if (l != 0) {
386 		if (l & 0x7f)
387 		    fprintf(stderr, " status %d", l & 0x7f);
388 		if (l & 0xff00)
389 		    fprintf(stderr, " exit code %d", (l>>8) & 0xff);
390 		exit_status = 4;
391 	    }
392 	    fputc('\n', stderr);
393 	}
394     }
395     alarm(0);
396     wrapup("Finished waiting ...");
397 
398     exit(0);
399 }
400 
onalarm(int foo)401 void onalarm(int foo)
402 {
403     thres += est_rate;
404     signal(SIGALRM, onalarm);
405     alarm(GRANULE);
406 }
407 
grunt()408 void grunt()
409 {
410     /* timeout after label "bepatient" in main */
411     exit_status = 4;
412     wrapup("Timed out waiting for jobs to finish ...");
413 }
414 
pipeerr()415 void pipeerr()
416 {
417 	sigpipe++;
418 }
419 
wrapup(const char * reason)420 void wrapup(const char *reason)
421 {
422     int i;
423     int killed = 0;
424     fflush(stderr);
425     for (i = 0; i < nusers; i++) {
426 	if (child[i].pid > 0 && kill(child[i].pid, SIGKILL) != -1) {
427 	    if (!killed) {
428 		killed++;
429 		fprintf(stderr, "%s\n", reason);
430 		fflush(stderr);
431 	    }
432 	    fprintf(stderr, "user %d job %d pid %d killed off\n", firstuser+i, child[i].thisjob, child[i].pid);
433 	fflush(stderr);
434 	}
435     }
436     exit(exit_status);
437 }
438 
439 #define MAXLINE 512
getwork(void)440 void getwork(void)
441 {
442     int			i;
443     int			f;
444     int			ac=0;
445     char		*lp = (void *)0;
446     char		*q = (void *)0;
447     struct st_work	*w = (void *)0;
448     char		line[MAXLINE];
449 
450     while (fgets(line, MAXLINE, stdin) != NULL) {
451 	if (nwork >= MAXWORK) {
452 	    fprintf(stderr, "Too many jobs specified, .. increase MAXWORK\n");
453 	    exit(4);
454 	}
455 	w = &work[nwork];
456 	lp = line;
457 	i = 1;
458 	while (*lp && *lp != ' ') {
459 	    i++;
460 	    lp++;
461 	}
462 	w->cmd = (char *)malloc(i);
463 	strncpy(w->cmd, line, i-1);
464 	w->cmd[i-1] = '\0';
465 	w->inpsize = 0;
466 	w->input = "";
467 	/* start to build arg list */
468 	ac = 2;
469 	w->av = (char **)malloc(2*sizeof(char *));
470 	q = w->cmd;
471 	while (*q) q++;
472 	q--;
473 	while (q >= w->cmd) {
474 	    if (*q == '/') {
475 		q++;
476 		break;
477 	    }
478 	    q--;
479 	}
480 	w->av[0] = q;
481 	while (*lp) {
482 	    if (*lp == ' ') {
483 		/* space */
484 		lp++;
485 		continue;
486 	    }
487 	    else if (*lp == '<') {
488 		/* standard input for this job */
489 		q = ++lp;
490 		while (*lp && *lp != ' ') lp++;
491 		*lp = '\0';
492 		if ((f = open(q, 0)) == -1) {
493 		    fprintf(stderr, "cannot open input file (%s) for job %d\n",
494 				q, nwork);
495 		    exit(4);
496 		}
497 		/* gobble input */
498 		w->input = (char *)malloc(512);
499 		while ((i = read(f, &w->input[w->inpsize], 512)) > 0) {
500 		    w->inpsize += i;
501 		    w->input = (char *)realloc(w->input, w->inpsize+512);
502 		}
503 		w->input = (char *)realloc(w->input, w->inpsize);
504 		close(f);
505 		/* extract stdout file name from line beginning "C=" */
506 		w->outf = "";
507 		for (q = w->input; q < &w->input[w->inpsize-10]; q++) {
508 		    if (*q == '\n' && strncmp(&q[1], "C=", 2) == 0) {
509 			w->outf = &q[3];
510 			break;
511 		    }
512 		}
513 #if debug
514 		if (*w->outf) {
515 		    fprintf(stderr, "stdout->");
516 		    for (q=w->outf; *q != '\n'; q++)
517 			fputc(*q, stderr);
518 		    fputc('\n', stderr);
519 		}
520 #endif
521 	    }
522 	    else {
523 		/* a command option */
524 		ac++;
525 		w->av = (char **)realloc(w->av, ac*sizeof(char *));
526 		q = lp;
527 		i = 1;
528 		while (*lp && *lp != ' ') {
529 		    lp++;
530 		    i++;
531 		}
532 		w->av[ac-2] = (char *)malloc(i);
533 		strncpy(w->av[ac-2], q, i-1);
534 		w->av[ac-2][i-1] = '\0';
535 	    }
536 	}
537 	w->av[ac-1] = (char *)0;
538 	nwork++;
539     }
540 }
541 
542 #if debug
dumpwork(void)543 void dumpwork(void)
544 {
545     int		i;
546     int		j;
547 
548     for (i = 0; i < nwork; i++) {
549 	fprintf(stderr, "job %d: cmd: %s\n", i, work[i].cmd);
550 	j = 0;
551 	while (work[i].av[j]) {
552 		fprintf(stderr, "argv[%d]: %s\n", j, work[i].av[j]);
553 		j++;
554 	}
555 	fprintf(stderr, "input: %d chars text: ", work[i].inpsize);
556 	if (work[i].input == (char *)0)
557 		fprintf(stderr, "<NULL>\n");
558 	else {
559 	        register char	*pend;
560 	        char		*p;
561 		char		c;
562 		p = work[i].input;
563 		while (*p) {
564 			pend = p;
565 			while (*pend && *pend != '\n')
566 				pend++;
567 			c = *pend;
568 			*pend = '\0';
569 			fprintf(stderr, "%s\n", p);
570 			*pend = c;
571 			p = &pend[1];
572 		}
573 	}
574     }
575 }
576 #endif
577 
fatal(const char * s)578 void fatal(const char *s)
579 {
580     int	i;
581     fprintf(stderr, "%s", s);
582     fflush(stderr);
583     perror("Reason?");
584     fflush(stderr);
585     for (i = 0; i < nusers; i++) {
586 	if (child[i].pid > 0 && kill(child[i].pid, SIGKILL) != -1) {
587 	    fprintf(stderr, "pid %d killed off\n", child[i].pid);
588 	    fflush(stderr);
589 	}
590     }
591     exit_status = 4;
592 }
593