1 /* @(#)limit.c	1.45 21/07/13 Copyright 1987-2021 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)limit.c	1.45 21/07/13 Copyright 1987-2021 J. Schilling";
6 #endif
7 /*
8  *	Resource usage routines
9  *
10  *	Copyright (c) 1987-2021 J. Schilling
11  */
12 /*
13  * The contents of this file are subject to the terms of the
14  * Common Development and Distribution License, Version 1.0 only
15  * (the "License").  You may not use this file except in compliance
16  * with the License.
17  *
18  * See the file CDDL.Schily.txt in this distribution for details.
19  * A copy of the CDDL is also available via the Internet at
20  * http://www.opensource.org/licenses/cddl1.txt
21  *
22  * When distributing Covered Code, include this CDDL HEADER in each
23  * file and include the License file CDDL.Schily.txt from this distribution.
24  */
25 
26 /*
27  * XXX SGI kann 64 bit resources,
28  * XXX Solaris kann kein 64 bit proc file (LF32)
29  * XXX daher ist getrusage() jetzt in getrusage.c
30  */
31 
32 #include <schily/stdio.h>
33 #include <schily/utypes.h>
34 #include <schily/unistd.h>
35 #include <schily/fcntl.h>
36 #include <schily/time.h>
37 #include <schily/resource.h>
38 #include "bsh.h"
39 #include "str.h"
40 #include "strsubs.h"
41 
42 
43 #	ifndef	HAVE_GETRLIMIT
44 #		define	getrlimit	_getrlimit
45 #		endif
46 #	ifndef	HAVE_SETRLIMIT
47 #		define	setrlimit	_setrlimit
48 #	endif
49 #include "limit.h"
50 
51 
52 #ifndef	RLIM_INFINITY
53 #define	RLIM_INFINITY	0x7FFFFFFF
54 #endif
55 
56 typedef struct {
57 	char	*l_name;
58 	int	l_which;
59 	int	l_factor;
60 	char	*l_scale;
61 } LIMIT;
62 
63 LIMIT	limits[] = {
64 #ifdef	RLIMIT_CPU
65 	{	"cputime",	RLIMIT_CPU,	1,	"seconds"	},
66 #endif
67 #ifdef	RLIMIT_FSIZE
68 	{	"filesize",	RLIMIT_FSIZE,	1024,	"kBytes"	},
69 #endif
70 #ifdef	RLIMIT_DATA
71 	{	"datasize",	RLIMIT_DATA,	1024,	"kBytes"	},
72 #endif
73 #ifdef	RLIMIT_STACK
74 	{	"stacksize",	RLIMIT_STACK,	1024,	"kBytes"	},
75 #endif
76 #ifdef	RLIMIT_CORE
77 	{	"coredumpsize",	RLIMIT_CORE,	1024,	"kBytes"	},
78 #endif
79 #ifdef	RLIMIT_RSS
80 	{	"memoryuse",	RLIMIT_RSS,	1024,	"kBytes"	},
81 #endif
82 #if	defined(RLIMIT_UMEM) && !!defined(RLIMIT_RSS)	/* SUPER UX */
83 	{	"memoryuse",	RLIMIT_UMEM,	1024,	"kBytes"	},
84 #endif
85 #ifdef	RLIMIT_NOFILE
86 	{	"descriptors",	RLIMIT_NOFILE,	1,	""		},
87 #endif
88 #if	defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
89 	{	"descriptors",	RLIMIT_OFILE,	1,	""		},
90 #endif
91 #ifdef	RLIMIT_VMEM
92 	{	"vmemsize",	RLIMIT_VMEM,	1024,	"kBytes"	},
93 #endif
94 #if	defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
95 	{	"vmemsize",	RLIMIT_AS,	1024,	"kBytes"	},
96 #endif
97 #ifdef	RLIMIT_HEAP	/* BS2000/OSD */
98 	{	"heapsize",	RLIMIT_HEAP,	1024,	"kBytes"	},
99 #endif
100 #ifdef	RLIMIT_CONCUR	/* CONVEX max. # of processors per process */
101 	{	"concurrency",	RLIMIT_CONCUR,	1,	"thread(s)"	},
102 #endif
103 #ifdef	RLIMIT_NPROC
104 	{	"nproc",	RLIMIT_NPROC,	1,	""		},
105 #endif
106 #ifdef	RLIMIT_MEMLOCK
107 	{	"memorylocked",	RLIMIT_MEMLOCK,	1024,	"kBytes"	},
108 #endif
109 #ifdef	RLIMIT_LOCKS
110 	{	"filelocks",	RLIMIT_LOCKS,	1,	""		},
111 #endif
112 #ifdef	RLIMIT_SIGPENDING
113 	{	"sigpending",	RLIMIT_SIGPENDING, 1,	""		},
114 #endif
115 #ifdef	RLIMIT_MSGQUEUE
116 	{	"msgqueues",	RLIMIT_MSGQUEUE, 1024,	"kBytes"	},
117 #endif
118 	/* Nice levels 19 .. -20 correspond to 0 .. 39 */
119 #ifdef	RLIMIT_NICE
120 	{	"maxnice",	RLIMIT_NICE,	1,	""		},
121 #endif
122 #ifdef	RLIMIT_RTPRIO
123 	{	"maxrtprio",	RLIMIT_RTPRIO,	1,	""		},
124 #endif
125 #ifdef	RLIMIT_RTTIME
126 	{	"maxrttime",	RLIMIT_RTTIME,	1,	"usec(s)"	},
127 #endif
128 #ifdef	RLIMIT_SBSIZE	/* FreeBSD maximum size of all socket buffers */
129 	{	"sbsize",	RLIMIT_SBSIZE,	1,	""		},
130 #endif
131 #ifdef	RLIMIT_NPTS	/* FreeBSD maximum # of pty's */
132 	{	"npts",		RLIMIT_NPTS,	1,	""		},
133 #endif
134 #ifdef	RLIMIT_SWAP	/* FreeBSD swap used */
135 	{	"swap",		RLIMIT_SWAP,	1024,	"kBytes"	},
136 #endif
137 #ifdef	RLIMIT_KQUEUES	/* FreeBSD kqueues allocated */
138 	{	"kqueues",	RLIMIT_KQUEUES,	1,	""	},
139 #endif
140 #ifdef	RLIMIT_UMTXP	/* FreeBSD process-shared umtx */
141 	{	"umtx shared locks", RLIMIT_UMTXP, 1,	""	},
142 #endif
143 };
144 
145 int	nlimits = sizeof (limits)/sizeof (limits[0]);
146 
147 char	unlimited[] = "unlimited";
148 
149 struct timeval	starttime;
150 struct timeval	pt;
151 long		pagesize;
152 
153 EXPORT	void	blimit		__PR((Argvec * vp, FILE ** std, int flag));
154 LOCAL	LIMIT	*limwhich	__PR((FILE ** std, char *s));
155 LOCAL	BOOL	getllval	__PR((FILE ** std, LIMIT * limit, char *s, Ullong *llp));
156 LOCAL	BOOL	getlimit	__PR((FILE ** std, LIMIT * limit, struct rlimit *limp));
157 LOCAL	BOOL	setlimit	__PR((FILE ** std, LIMIT * limit, struct rlimit *limp));
158 LOCAL	void	printlimit	__PR((FILE ** std, LIMIT * limit, struct rlimit *limp));
159 EXPORT	void	inittime	__PR((void));
160 EXPORT	void	setstime	__PR((void));
161 EXPORT	void	prtime		__PR((FILE ** std, long sec, long usec));
162 EXPORT	void	getpruself	__PR((struct rusage *prusage));
163 EXPORT	void	getpruchld	__PR((struct rusage *prusage));
164 EXPORT	void	btime		__PR((Argvec * vp, FILE ** std, int flag));
165 EXPORT	void	prtimes		__PR((FILE ** std, struct rusage *prusage));
166 LOCAL	void	prtm		__PR((FILE ** std, struct rusage *prusage, struct timeval *stt));
167 EXPORT	void	rusagesub	__PR((struct rusage *pru1, struct rusage *pru2));
168 EXPORT	void	rusageadd	__PR((struct rusage *pru1, struct rusage *pru2));
169 
170 /* ARGSUSED */
171 EXPORT void
blimit(vp,std,flag)172 blimit(vp, std, flag)
173 	register	Argvec	*vp;
174 			FILE	*std[];
175 			int	flag;
176 {
177 	register int	i;
178 	register LIMIT	*limit;
179 	struct rlimit	lim;
180 	Ullong		l_cur;
181 	Ullong		l_max;
182 
183 	if (vp->av_ac > 4) {
184 		wrong_args(vp, std);
185 		return;
186 	}
187 	if (vp->av_ac == 1) {
188 		for (i = 0, limit = limits; i < nlimits; i++, limit++) {
189 			if (getlimit(std, limit, &lim))
190 				printlimit(std, limit, &lim);
191 		}
192 	} else if (vp->av_ac >= 2) {
193 		if ((limit = limwhich(std, vp->av_av[1])) == (LIMIT *)NULL)
194 			return;
195 		if (!getlimit(std, limit, &lim))
196 			return;
197 		l_cur = lim.rlim_cur;
198 		l_max = lim.rlim_max;
199 		if (vp->av_ac == 2)
200 			printlimit(std, limit, &lim);
201 		else if (!getllval(std, limit, vp->av_av[2], &l_cur))
202 			return;
203 		else if (vp->av_ac == 4 &&
204 			!getllval(std, limit, vp->av_av[3], &l_max))
205 			return;
206 		else {
207 			lim.rlim_cur = l_cur;
208 			lim.rlim_max = l_max;
209 			if (lim.rlim_cur == RLIM_INFINITY && geteuid() != 0)
210 				lim.rlim_cur = lim.rlim_max;
211 			setlimit(std, limit, &lim);
212 		}
213 	}
214 }
215 
216 LOCAL LIMIT *
limwhich(std,s)217 limwhich(std, s)
218 	FILE	*std[];
219 	char	*s;
220 {
221 	register int	i;
222 	register LIMIT	*limit;
223 	register LIMIT	*flimit	= (LIMIT *)NULL;
224 	register BOOL	found	= FALSE;
225 
226 	for (i = 0, limit = limits; i < nlimits; i++, limit++) {
227 		if (strbeg(s, limit->l_name)) {
228 			if (found) {
229 				fprintf(std[2], "%s\n", eambiguous);
230 				return ((LIMIT *)NULL);
231 			}
232 			flimit = limit;
233 			found = TRUE;
234 		}
235 	}
236 	if (found)
237 		return (flimit);
238 	fprintf(std[2], "No such limit.\n");
239 	ex_status = 1;
240 	return ((LIMIT *) NULL);
241 }
242 
243 LOCAL BOOL
getllval(std,limit,s,llp)244 getllval(std, limit, s, llp)
245 	FILE	*std[];
246 	LIMIT	*limit;
247 	char	*s;
248 	Ullong	*llp;
249 {
250 	Llong	ll;
251 
252 	if (strbeg(s, unlimited)) {
253 		*llp = RLIM_INFINITY;
254 		return (TRUE);
255 	}
256 	if (!tollong(std, s, &ll))
257 		return (FALSE);
258 	*llp = ll * limit->l_factor;
259 	return (TRUE);
260 }
261 
262 LOCAL BOOL
getlimit(std,limit,limp)263 getlimit(std, limit, limp)
264 		FILE	*std[];
265 		LIMIT	*limit;
266 	struct rlimit	*limp;
267 {
268 	if (getrlimit(limit->l_which, limp) < 0) {
269 		ex_status = geterrno();
270 		fprintf(std[2], "Can't get %s limit. %s\n",
271 				limit->l_name, errstr(ex_status));
272 		return (FALSE);
273 	}
274 	return (TRUE);
275 }
276 
277 LOCAL BOOL
setlimit(std,limit,limp)278 setlimit(std, limit, limp)
279 		FILE	*std[];
280 		LIMIT	*limit;
281 	struct rlimit	*limp;
282 {
283 	if (setrlimit(limit->l_which, limp) < 0) {
284 		ex_status = geterrno();
285 		fprintf(std[2], "Can't set %s limit. %s\n",
286 				limit->l_name, errstr(ex_status));
287 		return (FALSE);
288 	}
289 	return (TRUE);
290 }
291 
292 LOCAL void
printlimit(std,limit,limp)293 printlimit(std, limit, limp)
294 		FILE	*std[];
295 	register LIMIT	*limit;
296 	register struct	rlimit	*limp;
297 {
298 
299 	fprintf(std[1], "%-14s", limit->l_name);
300 
301 	if (limp->rlim_cur == RLIM_INFINITY)
302 		fprintf(std[1], "%14s\t", unlimited);
303 #ifdef	RLIMIT_CPU
304 	else if (limit->l_which == RLIMIT_CPU) {
305 		prtime(std, (long)limp->rlim_cur, 0L);
306 		fprintf(std[1], "\t");
307 	}
308 #endif
309 	else {
310 		fprintf(std[1], "%-7ld %6s\t",
311 			(long)(limp->rlim_cur/limit->l_factor), limit->l_scale);
312 	}
313 	if (limp->rlim_max == RLIM_INFINITY)
314 		fprintf(std[1], "%14s\n", unlimited);
315 #ifdef	RLIMIT_CPU
316 	else if (limit->l_which == RLIMIT_CPU) {
317 		prtime(std, (long)limp->rlim_max, 0L);
318 		fprintf(std[1], "\n");
319 	}
320 #endif
321 	else {
322 		fprintf(std[1], "%-7ld %6s\n",
323 			(long)(limp->rlim_max/limit->l_factor), limit->l_scale);
324 	}
325 }
326 
327 #ifndef	HAVE_GETRLIMIT
getrlimit(which,limp)328 getrlimit(which, limp)
329 		int	which;
330 	struct rlimit	*limp;
331 {
332 	int	val;
333 
334 	switch (which) {
335 
336 	case RLIMIT_FSIZE:
337 #ifdef	HAVE_ULIMIT
338 			if ((val = ulimit(1, 0)) < 0)
339 				return (-1);
340 			limp->rlim_cur = limp->rlim_max = val * 512;
341 			break;
342 #else
343 			seterrno(EINVAL);
344 			return (-1);
345 #endif
346 	default:	limp->rlim_cur = limp->rlim_max = RLIM_INFINITY;
347 	}
348 	return (0);
349 }
350 #endif
351 
352 #ifndef	HAVE_SETRLIMIT
setrlimit(which,limp)353 setrlimit(which, limp)
354 		int	which;
355 	struct rlimit	*limp;
356 {
357 	switch (which) {
358 
359 	case RLIMIT_FSIZE:
360 #ifdef	HAVE_ULIMIT
361 			return (ulimit(2, limp->rlim_cur/512));
362 #else
363 			break;
364 #endif
365 	}
366 	seterrno(EINVAL);
367 	return (-1);
368 }
369 #endif
370 
371 
372 EXPORT void
inittime()373 inittime()
374 {
375 	gettimeofday(&starttime, (struct timezone *)0);
376 
377 #ifdef	_SC_PAGESIZE
378 	pagesize = sysconf(_SC_PAGESIZE)/1024;
379 #else
380 	pagesize = getpagesize()/1024;
381 #endif
382 	if (pagesize == 0)
383 		pagesize = 1;
384 }
385 
386 EXPORT void
setstime()387 setstime()
388 {
389 	gettimeofday(&pt, (struct timezone *)0);
390 }
391 
392 EXPORT void
prtime(std,sec,usec)393 prtime(std, sec, usec)
394 	FILE	*std[];
395 	long	sec;
396 	long	usec;
397 {
398 	long	hour;
399 	long	min;
400 
401 	hour = sec/3600;
402 	min = sec/60%60;
403 	sec %= 60;
404 	if (hour)
405 		fprintf(std[1], "%ld:%02ld:%02ld", hour, min, sec);
406 	else if (min)
407 		fprintf(std[1], "%ld:%02ld", min, sec);
408 	else
409 		fprintf(std[1], "%ld", sec);
410 	if (usec)
411 		fprintf(std[1], ".%03ld", usec/1000);
412 }
413 
414 #if defined(HAVE_GETRUSAGE) || defined(PIOCUSAGE)
415 
416 EXPORT void
getpruself(prusage)417 getpruself(prusage)
418 	struct rusage *prusage;
419 {
420 	getrusage(RUSAGE_SELF, prusage);
421 }
422 
423 EXPORT void
getpruchld(prusage)424 getpruchld(prusage)
425 	struct rusage *prusage;
426 {
427 	getrusage(RUSAGE_CHILDREN, prusage);
428 }
429 
430 /* ARGSUSED */
431 EXPORT void
btime(vp,std,flag)432 btime(vp, std, flag)
433 	register	Argvec	*vp;
434 			FILE	*std[];
435 			int	flag;
436 {
437 /*	struct	timeval	stoptime;*/
438 	struct	rusage	rusage;
439 
440 	if (vp->av_ac == 1) {
441 		getrusage(RUSAGE_SELF, &rusage);
442 /*		gettimeofday(&stoptime, (struct timezone *)0);*/
443 		prtm(std, &rusage, &starttime);
444 
445 		getrusage(RUSAGE_CHILDREN, &rusage);
446 		prtm(std, &rusage, &starttime);
447 	}
448 }
449 
450 #else
451 
452 EXPORT void
getpruself(prusage)453 getpruself(prusage)
454 	struct rusage *prusage;
455 {
456 	fillbytes(&prusage, sizeof (*prusage), '\0');
457 }
458 
459 EXPORT void
getpruchld(prusage)460 getpruchld(prusage)
461 	struct rusage *prusage;
462 {
463 	fillbytes(&prusage, sizeof (*prusage), '\0');
464 }
465 
466 /* ARGSUSED */
467 EXPORT void
btime(vp,std,flag)468 btime(vp, std, flag)
469 	register	Argvec	*vp;
470 			FILE	*std[];
471 			int	flag;
472 {
473 	unimplemented(vp, std);
474 }
475 #endif	/* ! defined(HAVE_GETRUSAGE) || defined(PIOCUSAGE) */
476 
477 EXPORT void
prtimes(std,prusage)478 prtimes(std, prusage)
479 		FILE	*std[];
480 	struct rusage *prusage;
481 {
482 	prtm(std, prusage, &pt);
483 }
484 
485 LOCAL void
prtm(std,prusage,stt)486 prtm(std, prusage, stt)
487 	FILE	*std[];
488 	register struct rusage *prusage;
489 	register struct timeval *stt;
490 {
491 	long	cpu;
492 	long	sec;
493 	long	usec;
494 	long	tmsec;
495 	long	tics;
496 	struct	timeval	stoptime;
497 
498 	gettimeofday(&stoptime, (struct timezone *)0);
499 	sec = stoptime.tv_sec - stt->tv_sec;
500 	usec = stoptime.tv_usec - stt->tv_usec;
501 	tmsec = sec * 1000 + usec/1000;
502 	if (tmsec < 100)		/* avoid floating exception */
503 		tmsec = 100;
504 	if (usec < 0) {
505 		sec--;
506 		usec += 1000000;
507 	}
508 	cpu = prusage->ru_utime.tv_sec*1000 + prusage->ru_utime.tv_usec/1000;
509 	cpu += prusage->ru_stime.tv_sec*1000 + prusage->ru_stime.tv_usec/1000;
510 
511 	tics = cpu/(1000/50);
512 	tics /= pagesize;		/* for ixrss & idrss		*/
513 	if (tics == 0)			/* avoid floating exception	*/
514 		tics = 1;
515 
516 	prtime(std, sec, usec|1);	/* print usec always */
517 
518 #if defined(__BEOS__) || defined(__HAIKU__) || \
519     defined(OS390) || defined(__MVS__)
520 	/* XXX dirty hack */
521 	fprintf(std[1],
522 		"r %ld.%03ldu %ld.%03lds %ld%%\n",
523 		(long)prusage->ru_utime.tv_sec,
524 		(long)prusage->ru_utime.tv_usec/1000,
525 		(long)prusage->ru_stime.tv_sec,
526 		(long)prusage->ru_stime.tv_usec/1000,
527 		cpu/(tmsec/100));
528 #else
529 	fprintf(std[1],
530 		"r %ld.%03ldu %ld.%03lds %ld%% %ldM %ld+%ldk %ldst %ld+%ldio %ldpf+%ldw\n",
531 		(long)prusage->ru_utime.tv_sec,
532 		(long)prusage->ru_utime.tv_usec/1000,
533 		(long)prusage->ru_stime.tv_sec,
534 		(long)prusage->ru_stime.tv_usec/1000,
535 		cpu/(tmsec/100),
536 		prusage->ru_maxrss*pagesize,
537 		prusage->ru_ixrss/tics, /* tics contains pagesize	*/
538 		prusage->ru_idrss/tics, /* tics contains pagesize	*/
539 		prusage->ru_isrss/tics, /* tics contains pagesize	*/
540 		prusage->ru_inblock,
541 		prusage->ru_oublock,
542 		prusage->ru_majflt,
543 		prusage->ru_nswap);
544 #endif
545 }
546 
547 EXPORT void
rusagesub(pru1,pru2)548 rusagesub(pru1, pru2)
549 	register struct rusage *pru1;
550 	register struct rusage *pru2;
551 {
552 	pru2->ru_utime.tv_sec	-= pru1->ru_utime.tv_sec;
553 	pru2->ru_utime.tv_usec	-= pru1->ru_utime.tv_usec;
554 	if (pru2->ru_utime.tv_usec < 0) {
555 		pru2->ru_utime.tv_sec -= 1;
556 		pru2->ru_utime.tv_usec += 1000000;
557 	}
558 	pru2->ru_stime.tv_sec	-= pru1->ru_stime.tv_sec;
559 	pru2->ru_stime.tv_usec	-= pru1->ru_stime.tv_usec;
560 	if (pru2->ru_stime.tv_usec < 0) {
561 		pru2->ru_stime.tv_sec -= 1;
562 		pru2->ru_stime.tv_usec += 1000000;
563 	}
564 #if defined(__BEOS__) || defined(__HAIKU__) || \
565     defined(OS390) || defined(__MVS__)
566 	/* XXX dirty hack */
567 #else
568 	pru2->ru_maxrss		-= pru1->ru_maxrss;
569 	pru2->ru_ixrss		-= pru1->ru_ixrss;
570 	pru2->ru_idrss		-= pru1->ru_idrss;
571 	pru2->ru_isrss		-= pru1->ru_isrss;
572 	pru2->ru_inblock	-= pru1->ru_inblock;
573 	pru2->ru_oublock	-= pru1->ru_oublock;
574 	pru2->ru_majflt		-= pru1->ru_majflt;
575 	pru2->ru_nswap		-= pru1->ru_nswap;
576 #endif
577 }
578 
579 EXPORT void
rusageadd(pru1,pru2)580 rusageadd(pru1, pru2)
581 	register struct rusage *pru1;
582 	register struct rusage *pru2;
583 {
584 	pru2->ru_utime.tv_sec	+= pru1->ru_utime.tv_sec;
585 	pru2->ru_utime.tv_usec	+= pru1->ru_utime.tv_usec;
586 	if (pru2->ru_utime.tv_usec >= 1000000) {
587 		pru2->ru_utime.tv_sec += 1;
588 		pru2->ru_utime.tv_usec -= 1000000;
589 	}
590 	pru2->ru_stime.tv_sec	+= pru1->ru_stime.tv_sec;
591 	pru2->ru_stime.tv_usec	+= pru1->ru_stime.tv_usec;
592 	if (pru2->ru_stime.tv_usec >= 1000000) {
593 		pru2->ru_stime.tv_sec += 1;
594 		pru2->ru_stime.tv_usec -= 1000000;
595 	}
596 #if defined(__BEOS__) || defined(__HAIKU__) || \
597     defined(OS390) || defined(__MVS__)
598 	/* XXX dirty hack */
599 #else
600 	pru2->ru_maxrss		+= pru1->ru_maxrss;
601 	pru2->ru_ixrss		+= pru1->ru_ixrss;
602 	pru2->ru_idrss		+= pru1->ru_idrss;
603 	pru2->ru_isrss		+= pru1->ru_isrss;
604 	pru2->ru_inblock	+= pru1->ru_inblock;
605 	pru2->ru_oublock	+= pru1->ru_oublock;
606 	pru2->ru_majflt		+= pru1->ru_majflt;
607 	pru2->ru_nswap		+= pru1->ru_nswap;
608 #endif
609 }
610