1/* ulimit.c, created from ulimit.def. */
2#line 23 "ulimit.def"
3
4#line 61 "ulimit.def"
5
6#if !defined (_MINIX)
7
8#include <config.h>
9
10#include "../bashtypes.h"
11#ifndef _MINIX
12#  include <sys/param.h>
13#endif
14
15#if defined (HAVE_UNISTD_H)
16#  include <unistd.h>
17#endif
18
19#include <stdio.h>
20#include <errno.h>
21
22#include "../bashintl.h"
23
24#include "../shell.h"
25#include "common.h"
26#include "bashgetopt.h"
27#include "pipesize.h"
28
29#if !defined (errno)
30extern int errno;
31#endif
32
33/* For some reason, HPUX chose to make these definitions visible only if
34   _KERNEL is defined, so we define _KERNEL before including <sys/resource.h>
35   and #undef it afterward. */
36#if defined (HAVE_RESOURCE)
37#  include <sys/time.h>
38#  if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
39#    define _KERNEL
40#  endif
41#  include <sys/resource.h>
42#  if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
43#    undef _KERNEL
44#  endif
45#else
46#  include <sys/times.h>
47#endif
48
49#if defined (HAVE_LIMITS_H)
50#  include <limits.h>
51#endif
52
53/* Check for the most basic symbols.  If they aren't present, this
54   system's <sys/resource.h> isn't very useful to us. */
55#if !defined (RLIMIT_FSIZE) || !defined (HAVE_GETRLIMIT)
56#  undef HAVE_RESOURCE
57#endif
58
59#if !defined (RLIMTYPE)
60#  define RLIMTYPE long
61#  define string_to_rlimtype(s) strtol(s, (char **)NULL, 10)
62#  define print_rlimtype(num, nl) printf ("%ld%s", num, nl ? "\n" : "")
63#endif
64
65/* Some systems use RLIMIT_NOFILE, others use RLIMIT_OFILE */
66#if defined (HAVE_RESOURCE) && defined (RLIMIT_OFILE) && !defined (RLIMIT_NOFILE)
67#  define RLIMIT_NOFILE RLIMIT_OFILE
68#endif /* HAVE_RESOURCE && RLIMIT_OFILE && !RLIMIT_NOFILE */
69
70/* Some systems have these, some do not. */
71#ifdef RLIMIT_FSIZE
72#  define RLIMIT_FILESIZE	RLIMIT_FSIZE
73#else
74#  define RLIMIT_FILESIZE	256
75#endif
76
77#define RLIMIT_PIPESIZE	257
78
79#ifdef RLIMIT_NOFILE
80#  define RLIMIT_OPENFILES	RLIMIT_NOFILE
81#else
82#  define RLIMIT_OPENFILES	258
83#endif
84
85#ifdef RLIMIT_VMEM
86#  define RLIMIT_VIRTMEM	RLIMIT_VMEM
87#  define RLIMIT_VMBLKSZ	1024
88#else
89#  ifdef RLIMIT_AS
90#    define RLIMIT_VIRTMEM	RLIMIT_AS
91#    define RLIMIT_VMBLKSZ	1024
92#  else
93#    define RLIMIT_VIRTMEM	259
94#    define RLIMIT_VMBLKSZ	1
95#  endif
96#endif
97
98#ifdef RLIMIT_NPROC
99#  define RLIMIT_MAXUPROC	RLIMIT_NPROC
100#else
101#  define RLIMIT_MAXUPROC	260
102#endif
103
104#if !defined (RLIM_INFINITY)
105#  define RLIM_INFINITY 0x7fffffff
106#endif
107
108#if !defined (RLIM_SAVED_CUR)
109#  define RLIM_SAVED_CUR RLIM_INFINITY
110#endif
111
112#if !defined (RLIM_SAVED_MAX)
113#  define RLIM_SAVED_MAX RLIM_INFINITY
114#endif
115
116#define LIMIT_HARD 0x01
117#define LIMIT_SOFT 0x02
118
119static int _findlim __P((int));
120
121static int ulimit_internal __P((int, char *, int, int));
122
123static int get_limit __P((int, RLIMTYPE *, RLIMTYPE *));
124static int set_limit __P((int, RLIMTYPE, int));
125
126static void printone __P((int, RLIMTYPE, int));
127static void print_all_limits __P((int));
128
129static int set_all_limits __P((int, RLIMTYPE));
130
131static int filesize __P((RLIMTYPE *));
132static int pipesize __P((RLIMTYPE *));
133static int getmaxuprc __P((RLIMTYPE *));
134static int getmaxvm __P((RLIMTYPE *, RLIMTYPE *));
135
136typedef struct {
137  int  option;			/* The ulimit option for this limit. */
138  int  parameter;		/* Parameter to pass to get_limit (). */
139  int  block_factor;		/* Blocking factor for specific limit. */
140  char *description;		/* Descriptive string to output. */
141  char *units;			/* scale */
142} RESOURCE_LIMITS;
143
144static RESOURCE_LIMITS limits[] = {
145#ifdef RLIMIT_CORE
146  { 'c',	RLIMIT_CORE,  1024,	"core file size",	"blocks" },
147#endif
148#ifdef RLIMIT_DATA
149  { 'd',	RLIMIT_DATA,  1024,	"data seg size",	"kbytes" },
150#endif
151#ifdef RLIMIT_NICE
152  { 'e',	RLIMIT_NICE,  1,	"scheduling priority",	(char *)NULL },
153#endif
154  { 'f',	RLIMIT_FILESIZE, 1024,	"file size",		"blocks" },
155#ifdef RLIMIT_SIGPENDING
156  { 'i',	RLIMIT_SIGPENDING, 1,	"pending signals",	(char *)NULL },
157#endif
158#ifdef RLIMIT_MEMLOCK
159  { 'l',	RLIMIT_MEMLOCK, 1024,	"max locked memory",	"kbytes" },
160#endif
161#ifdef RLIMIT_RSS
162  { 'm',	RLIMIT_RSS,   1024,	"max memory size",	"kbytes" },
163#endif /* RLIMIT_RSS */
164  { 'n',	RLIMIT_OPENFILES, 1,	"open files",		(char *)NULL},
165  { 'p',	RLIMIT_PIPESIZE, 512,	"pipe size", 		"512 bytes" },
166#ifdef RLIMIT_MSGQUEUE
167  { 'q',	RLIMIT_MSGQUEUE, 1,	"POSIX message queues",	"bytes" },
168#endif
169#ifdef RLIMIT_RTPRIO
170  { 'r',	RLIMIT_RTPRIO,  1,	"real-time priority",	(char *)NULL },
171#endif
172#ifdef RLIMIT_STACK
173  { 's',	RLIMIT_STACK, 1024,	"stack size",		"kbytes" },
174#endif
175#ifdef RLIMIT_CPU
176  { 't',	RLIMIT_CPU,      1,	"cpu time",		"seconds" },
177#endif /* RLIMIT_CPU */
178  { 'u',	RLIMIT_MAXUPROC, 1,	"max user processes",	(char *)NULL },
179#if defined (HAVE_RESOURCE)
180  { 'v',	RLIMIT_VIRTMEM, RLIMIT_VMBLKSZ, "virtual memory", "kbytes" },
181#endif
182#ifdef RLIMIT_SWAP
183  { 'w',	RLIMIT_SWAP,	1024,	"swap size",		"kbytes" },
184#endif
185#ifdef RLIMIT_LOCKS
186  { 'x',	RLIMIT_LOCKS,	1,	"file locks",		(char *)NULL },
187#endif
188  { -1, -1, -1, (char *)NULL, (char *)NULL }
189};
190#define NCMDS	(sizeof(limits) / sizeof(limits[0]))
191
192typedef struct _cmd {
193  int cmd;
194  char *arg;
195} ULCMD;
196
197static ULCMD *cmdlist;
198static int ncmd;
199static int cmdlistsz;
200
201#if !defined (HAVE_RESOURCE) && !defined (HAVE_ULIMIT)
202long
203ulimit (cmd, newlim)
204     int cmd;
205     long newlim;
206{
207  errno = EINVAL;
208  return -1;
209}
210#endif /* !HAVE_RESOURCE && !HAVE_ULIMIT */
211
212static int
213_findlim (opt)
214     int opt;
215{
216  register int i;
217
218  for (i = 0; limits[i].option > 0; i++)
219    if (limits[i].option == opt)
220      return i;
221  return -1;
222}
223
224static char optstring[4 + 2 * NCMDS];
225
226/* Report or set limits associated with certain per-process resources.
227   See the help documentation in builtins.c for a full description. */
228int
229ulimit_builtin (list)
230     register WORD_LIST *list;
231{
232  register char *s;
233  int c, limind, mode, opt, all_limits;
234
235  mode = 0;
236
237  all_limits = 0;
238
239  /* Idea stolen from pdksh -- build option string the first time called. */
240  if (optstring[0] == 0)
241    {
242      s = optstring;
243      *s++ = 'a'; *s++ = 'S'; *s++ = 'H';
244      for (c = 0; limits[c].option > 0; c++)
245	{
246	  *s++ = limits[c].option;
247	  *s++ = ';';
248	}
249      *s = '\0';
250    }
251
252  /* Initialize the command list. */
253  if (cmdlistsz == 0)
254    cmdlist = (ULCMD *)xmalloc ((cmdlistsz = 16) * sizeof (ULCMD));
255  ncmd = 0;
256
257  reset_internal_getopt ();
258  while ((opt = internal_getopt (list, optstring)) != -1)
259    {
260      switch (opt)
261	{
262	case 'a':
263	  all_limits++;
264	  break;
265
266	/* -S and -H are modifiers, not real options.  */
267	case 'S':
268	  mode |= LIMIT_SOFT;
269	  break;
270
271	case 'H':
272	  mode |= LIMIT_HARD;
273	  break;
274
275	case '?':
276	  builtin_usage ();
277	  return (EX_USAGE);
278
279	default:
280	  if (ncmd >= cmdlistsz)
281	    cmdlist = (ULCMD *)xrealloc (cmdlist, (cmdlistsz *= 2) * sizeof (ULCMD));
282	  cmdlist[ncmd].cmd = opt;
283	  cmdlist[ncmd++].arg = list_optarg;
284	  break;
285	}
286    }
287  list = loptend;
288
289  if (all_limits)
290    {
291#ifdef NOTYET
292      if (list)		/* setting */
293        {
294          if (STREQ (list->word->word, "unlimited") == 0)
295            {
296              builtin_error (_("%s: invalid limit argument"), list->word->word);
297              return (EXECUTION_FAILURE);
298            }
299          return (set_all_limits (mode == 0 ? LIMIT_SOFT|LIMIT_HARD : mode, RLIM_INFINITY));
300        }
301#endif
302      print_all_limits (mode == 0 ? LIMIT_SOFT : mode);
303      return (EXECUTION_SUCCESS);
304    }
305
306  /* default is `ulimit -f' */
307  if (ncmd == 0)
308    {
309      cmdlist[ncmd].cmd = 'f';
310      /* `ulimit something' is same as `ulimit -f something' */
311      cmdlist[ncmd++].arg = list ? list->word->word : (char *)NULL;
312      if (list)
313	list = list->next;
314    }
315
316  /* verify each command in the list. */
317  for (c = 0; c < ncmd; c++)
318    {
319      limind = _findlim (cmdlist[c].cmd);
320      if (limind == -1)
321	{
322	  builtin_error (_("`%c': bad command"), cmdlist[c].cmd);
323	  return (EX_USAGE);
324	}
325    }
326
327  for (c = 0; c < ncmd; c++)
328    if (ulimit_internal (cmdlist[c].cmd, cmdlist[c].arg, mode, ncmd > 1) == EXECUTION_FAILURE)
329      return (EXECUTION_FAILURE);
330
331  return (EXECUTION_SUCCESS);
332}
333
334static int
335ulimit_internal (cmd, cmdarg, mode, multiple)
336     int cmd;
337     char *cmdarg;
338     int mode, multiple;
339{
340  int opt, limind, setting;
341  int block_factor;
342  RLIMTYPE soft_limit, hard_limit, real_limit, limit;
343
344  setting = cmdarg != 0;
345  limind = _findlim (cmd);
346  if (mode == 0)
347    mode = setting ? (LIMIT_HARD|LIMIT_SOFT) : LIMIT_SOFT;
348  opt = get_limit (limind, &soft_limit, &hard_limit);
349  if (opt < 0)
350    {
351      builtin_error (_("%s: cannot get limit: %s"), limits[limind].description,
352						 strerror (errno));
353      return (EXECUTION_FAILURE);
354    }
355
356  if (setting == 0)	/* print the value of the specified limit */
357    {
358      printone (limind, (mode & LIMIT_SOFT) ? soft_limit : hard_limit, multiple);
359      return (EXECUTION_SUCCESS);
360    }
361
362  /* Setting the limit. */
363  if (STREQ (cmdarg, "hard"))
364    real_limit = hard_limit;
365  else if (STREQ (cmdarg, "soft"))
366    real_limit = soft_limit;
367  else if (STREQ (cmdarg, "unlimited"))
368    real_limit = RLIM_INFINITY;
369  else if (all_digits (cmdarg))
370    {
371      limit = string_to_rlimtype (cmdarg);
372      block_factor = limits[limind].block_factor;
373      real_limit = limit * block_factor;
374
375      if ((real_limit / block_factor) != limit)
376	{
377	  sh_erange (cmdarg, "limit");
378	  return (EXECUTION_FAILURE);
379	}
380    }
381  else
382    {
383      sh_invalidnum (cmdarg);
384      return (EXECUTION_FAILURE);
385    }
386
387  if (set_limit (limind, real_limit, mode) < 0)
388    {
389      builtin_error (_("%s: cannot modify limit: %s"), limits[limind].description,
390						    strerror (errno));
391      return (EXECUTION_FAILURE);
392    }
393
394  return (EXECUTION_SUCCESS);
395}
396
397static int
398get_limit (ind, softlim, hardlim)
399     int ind;
400     RLIMTYPE *softlim, *hardlim;
401{
402  RLIMTYPE value;
403#if defined (HAVE_RESOURCE)
404  struct rlimit limit;
405#endif
406
407  if (limits[ind].parameter >= 256)
408    {
409      switch (limits[ind].parameter)
410	{
411	case RLIMIT_FILESIZE:
412	  if (filesize (&value) < 0)
413	    return -1;
414	  break;
415	case RLIMIT_PIPESIZE:
416	  if (pipesize (&value) < 0)
417	    return -1;
418	  break;
419	case RLIMIT_OPENFILES:
420	  value = (RLIMTYPE)getdtablesize ();
421	  break;
422	case RLIMIT_VIRTMEM:
423	  return (getmaxvm (softlim, hardlim));
424	case RLIMIT_MAXUPROC:
425	  if (getmaxuprc (&value) < 0)
426	    return -1;
427	  break;
428	default:
429	  errno = EINVAL;
430	  return -1;
431	}
432      *softlim = *hardlim = value;
433      return (0);
434    }
435  else
436    {
437#if defined (HAVE_RESOURCE)
438      if (getrlimit (limits[ind].parameter, &limit) < 0)
439	return -1;
440      *softlim = limit.rlim_cur;
441      *hardlim = limit.rlim_max;
442#  if defined (HPUX9)
443      if (limits[ind].parameter == RLIMIT_FILESIZE)
444	{
445	  *softlim *= 512;
446	  *hardlim *= 512;			/* Ugh. */
447	}
448      else
449#  endif /* HPUX9 */
450      return 0;
451#else
452      errno = EINVAL;
453      return -1;
454#endif
455    }
456}
457
458static int
459set_limit (ind, newlim, mode)
460     int ind;
461     RLIMTYPE newlim;
462     int mode;
463{
464#if defined (HAVE_RESOURCE)
465   struct rlimit limit;
466   RLIMTYPE val;
467#endif
468
469  if (limits[ind].parameter >= 256)
470    switch (limits[ind].parameter)
471      {
472      case RLIMIT_FILESIZE:
473#if !defined (HAVE_RESOURCE)
474	return (ulimit (2, newlim / 512L));
475#else
476	errno = EINVAL;
477	return -1;
478#endif
479
480      case RLIMIT_OPENFILES:
481#if defined (HAVE_SETDTABLESIZE)
482#  if defined (__CYGWIN__)
483	/* Grrr... Cygwin declares setdtablesize as void. */
484	setdtablesize (newlim);
485	return 0;
486#  else
487	return (setdtablesize (newlim));
488#  endif
489#endif
490      case RLIMIT_PIPESIZE:
491      case RLIMIT_VIRTMEM:
492      case RLIMIT_MAXUPROC:
493      default:
494	errno = EINVAL;
495	return -1;
496      }
497  else
498    {
499#if defined (HAVE_RESOURCE)
500      if (getrlimit (limits[ind].parameter, &limit) < 0)
501	return -1;
502#  if defined (HPUX9)
503      if (limits[ind].parameter == RLIMIT_FILESIZE)
504	newlim /= 512;				/* Ugh. */
505#  endif /* HPUX9 */
506      val = (current_user.euid != 0 && newlim == RLIM_INFINITY &&
507	       (mode & LIMIT_HARD) == 0 &&		/* XXX -- test */
508	       (limit.rlim_cur <= limit.rlim_max))
509		 ? limit.rlim_max : newlim;
510      if (mode & LIMIT_SOFT)
511	limit.rlim_cur = val;
512      if (mode & LIMIT_HARD)
513	limit.rlim_max = val;
514
515      return (setrlimit (limits[ind].parameter, &limit));
516#else
517      errno = EINVAL;
518      return -1;
519#endif
520    }
521}
522
523static int
524getmaxvm (softlim, hardlim)
525     RLIMTYPE *softlim, *hardlim;
526{
527#if defined (HAVE_RESOURCE)
528  struct rlimit datalim, stacklim;
529
530  if (getrlimit (RLIMIT_DATA, &datalim) < 0)
531    return -1;
532
533  if (getrlimit (RLIMIT_STACK, &stacklim) < 0)
534    return -1;
535
536  /* Protect against overflow. */
537  *softlim = (datalim.rlim_cur / 1024L) + (stacklim.rlim_cur / 1024L);
538  *hardlim = (datalim.rlim_max / 1024L) + (stacklim.rlim_max / 1024L);
539  return 0;
540#else
541  errno = EINVAL;
542  return -1;
543#endif /* HAVE_RESOURCE */
544}
545
546static int
547filesize(valuep)
548     RLIMTYPE *valuep;
549{
550#if !defined (HAVE_RESOURCE)
551  long result;
552  if ((result = ulimit (1, 0L)) < 0)
553    return -1;
554  else
555    *valuep = (RLIMTYPE) result * 512;
556  return 0;
557#else
558  errno = EINVAL;
559  return -1;
560#endif
561}
562
563static int
564pipesize (valuep)
565     RLIMTYPE *valuep;
566{
567#if defined (PIPE_BUF)
568  /* This is defined on Posix systems. */
569  *valuep = (RLIMTYPE) PIPE_BUF;
570  return 0;
571#else
572#  if defined (_POSIX_PIPE_BUF)
573  *valuep = (RLIMTYPE) _POSIX_PIPE_BUF;
574  return 0;
575#  else
576#    if defined (PIPESIZE)
577  /* This is defined by running a program from the Makefile. */
578  *valuep = (RLIMTYPE) PIPESIZE;
579  return 0;
580#    else
581  errno = EINVAL;
582  return -1;
583#    endif /* PIPESIZE */
584#  endif /* _POSIX_PIPE_BUF */
585#endif /* PIPE_BUF */
586}
587
588static int
589getmaxuprc (valuep)
590     RLIMTYPE *valuep;
591{
592  long maxchild;
593
594  maxchild = getmaxchild ();
595  if (maxchild < 0)
596    {
597      errno = EINVAL;
598      return -1;
599    }
600  else
601    {
602      *valuep = (RLIMTYPE) maxchild;
603      return 0;
604    }
605}
606
607static void
608print_all_limits (mode)
609     int mode;
610{
611  register int i;
612  RLIMTYPE softlim, hardlim;
613
614  if (mode == 0)
615    mode |= LIMIT_SOFT;
616
617  for (i = 0; limits[i].option > 0; i++)
618    {
619      if (get_limit (i, &softlim, &hardlim) == 0)
620	printone (i, (mode & LIMIT_SOFT) ? softlim : hardlim, 1);
621      else if (errno != EINVAL)
622	builtin_error ("%s: cannot get limit: %s", limits[i].description,
623						   strerror (errno));
624    }
625}
626
627static void
628printone (limind, curlim, pdesc)
629     int limind;
630     RLIMTYPE curlim;
631     int pdesc;
632{
633  char unitstr[64];
634
635  if (pdesc)
636    {
637      if (limits[limind].units)
638	sprintf (unitstr, "(%s, -%c) ", limits[limind].units, limits[limind].option);
639      else
640        sprintf (unitstr, "(-%c) ", limits[limind].option);
641
642      printf ("%-20s %16s", limits[limind].description, unitstr);
643    }
644  if (curlim == RLIM_INFINITY)
645    puts ("unlimited");
646  else if (curlim == RLIM_SAVED_MAX)
647    puts ("hard");
648  else if (curlim == RLIM_SAVED_CUR)
649    puts ("soft");
650  else
651    print_rlimtype ((curlim / limits[limind].block_factor), 1);
652}
653
654/* Set all limits to NEWLIM.  NEWLIM currently must be RLIM_INFINITY, which
655   causes all limits to be set as high as possible depending on mode (like
656   csh `unlimit').  Returns -1 if NEWLIM is invalid, 0 if all limits
657   were set successfully, and 1 if at least one limit could not be set.
658
659   To raise all soft limits to their corresponding hard limits, use
660	ulimit -S -a unlimited
661   To attempt to raise all hard limits to infinity (superuser-only), use
662	ulimit -H -a unlimited
663   To attempt to raise all soft and hard limits to infinity, use
664	ulimit -a unlimited
665*/
666
667static int
668set_all_limits (mode, newlim)
669     int mode;
670     RLIMTYPE newlim;
671{
672  register int i;
673  int retval = 0;
674
675  if (newlim != RLIM_INFINITY)
676    {
677      errno = EINVAL;
678      return -1;
679    }
680
681  if (mode == 0)
682    mode = LIMIT_SOFT|LIMIT_HARD;
683
684  for (retval = i = 0; limits[i].option > 0; i++)
685    if (set_limit (i, newlim, mode) < 0)
686      {
687	builtin_error ("%s: cannot modify limit: %s", limits[i].description,
688						      strerror (errno));
689	retval = 1;
690      }
691  return retval;
692}
693
694#endif /* !_MINIX */
695