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