From 638df31ca462b032d59bde586254be5f3dc21e35 Mon Sep 17 00:00:00 2001 From: axel Date: Sat, 15 Oct 2005 08:33:01 +1000 Subject: [PATCH] Implement the ulimit builtin darcs-hash:20051014223301-ac50b-f4e6303dcfbe26318e23020444b54d80fed9f535.gz --- Makefile.in | 5 +- builtin.c | 11 +- builtin.h | 8 + builtin_ulimit.c | 453 +++++++++++++++++++++++++++++++++++++++++++++++ wutil.c | 119 +++++++++---- 5 files changed, 550 insertions(+), 46 deletions(-) create mode 100644 builtin_ulimit.c diff --git a/Makefile.in b/Makefile.in index b3a3b964d..3e9da5008 100644 --- a/Makefile.in +++ b/Makefile.in @@ -58,7 +58,7 @@ COMMON_OBJS := function.o builtin.o common.o complete.o env.o exec.o \ COMMON_OBJS_WITH_HEADER := builtin_help.o # main.c exists, but main.h does not, etc. -COMMON_OBJS_WITH_CODE := builtin_set.o builtin_commandline.o +COMMON_OBJS_WITH_CODE := builtin_set.o builtin_commandline.o builtin_ulimit.c # All objects that the system needs to build fish FISH_OBJS := $(COMMON_OBJS) $(COMMON_OBJS_WITH_CODE) $(COMMON_OBJS_WITH_HEADER) main.o @@ -87,7 +87,8 @@ BUILTIN_DOC_SRC := doc_src/source.txt doc_src/and.txt \ doc_src/functions.txt doc_src/if.txt doc_src/jobs.txt \ doc_src/not.txt doc_src/or.txt doc_src/random.txt \ doc_src/return.txt doc_src/read.txt doc_src/set.txt \ - doc_src/status.txt doc_src/switch.txt doc_src/while.txt + doc_src/status.txt doc_src/switch.txt doc_src/ulimit.txt \ + doc_src/while.txt # # Files generated by running doxygen on the files in $(BUILTIN_DOC_SRC) diff --git a/builtin.c b/builtin.c index 2908e19b7..e8c4af254 100644 --- a/builtin.c +++ b/builtin.c @@ -108,12 +108,8 @@ int builtin_count_args( wchar_t **argv ) return argc; } -/** - This function works like wperror, but it prints its result into - the sb_err string_buffer_t instead of to stderr. Used by the builtin - commands. -*/ -static void builtin_wperror( const wchar_t *s) + +void builtin_wperror( const wchar_t *s) { if( s != 0 ) { @@ -2797,6 +2793,7 @@ void builtin_init() hash_put( &builtin, L"bind", (void*) &builtin_bind ); hash_put( &builtin, L"random", (void*) &builtin_random ); hash_put( &builtin, L"status", (void*) &builtin_status ); + hash_put( &builtin, L"ulimit", (void*) &builtin_ulimit ); /* Builtins that are handled directly by the parser. They are @@ -2853,6 +2850,7 @@ void builtin_init() intern_static( L"or" ); intern_static( L"begin" ); intern_static( L"status" ); + intern_static( L"ulimit" ); builtin_help_init(); } @@ -2980,6 +2978,7 @@ const wchar_t *builtin_get_desc( const wchar_t *b ) hash_put( desc, L"and", L"Execute second command if first suceeds"); hash_put( desc, L"begin", L"Create a block of code" ); hash_put( desc, L"status", L"Return status information about fish" ); + hash_put( desc, L"ulimit", L"Set or get the shells resurce usage limits" ); } return hash_get( desc, b ); diff --git a/builtin.h b/builtin.h index 386797f50..58304644a 100644 --- a/builtin.h +++ b/builtin.h @@ -105,5 +105,13 @@ void builtin_print_help( wchar_t *cmd, string_buffer_t *b ); int builtin_set(wchar_t **argv); int builtin_commandline(wchar_t **argv); +int builtin_ulimit(wchar_t **argv); + +/** + This function works like wperror, but it prints its result into + the sb_err string_buffer_t instead of to stderr. Used by the builtin + commands. +*/ +void builtin_wperror( const wchar_t *s); #endif diff --git a/builtin_ulimit.c b/builtin_ulimit.c new file mode 100644 index 000000000..49eda1a6b --- /dev/null +++ b/builtin_ulimit.c @@ -0,0 +1,453 @@ +/** \file builtin_commandline.c Functions defining the commandline builtin + +Functions used for implementing the commandline builtin. + +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "util.h" +#include "builtin.h" +#include "common.h" +#include "wgetopt.h" + +struct resource_t +{ + int resource; + const wchar_t *desc; + wchar_t switch_char; +} + ; + +const static struct resource_t resource_arr[] = +{ + { + RLIMIT_CORE, L"Maximum size of core files created", L'c' + } + , + { + RLIMIT_DATA, L"Maximum size of a process’s data segment", L'd' + } + , + { + RLIMIT_FSIZE, L"Maximum size of files created by the shell", L'f' + } + , + { + RLIMIT_MEMLOCK, L"Maximum size that may be locked into memory", L'l' + } + , + { + RLIMIT_RSS, L"Maximum resident set size", L'm' + } + , + { + RLIMIT_NOFILE, L"Maximum number of open file descriptors", L'n' + } + , + { + RLIMIT_STACK, L"Maximum stack size", L's' + } + , + { + RLIMIT_CPU, L"Maximum amount of cpu time in seconds", L't' + } + , + { + RLIMIT_NPROC, L"Maximum number of processes available to a single user", L'u' + } + , + { + RLIMIT_AS, L"Maximum amount of virtual memory available to the shell", L'v' + } + , + { + 0, 0 + } +} + ; + +static int get_multiplier( int what ) +{ + if( ( what == RLIMIT_NPROC ) || + ( what == RLIMIT_NOFILE ) || + ( what == RLIMIT_CPU ) ) + { + return 1; + } + return 1024; +} + + +static rlim_t get( int resource, int hard ) +{ + struct rlimit ls; + + getrlimit( resource, &ls ); + + return hard ? ls.rlim_max:ls.rlim_cur; +} + +static void print( int resource, int hard ) +{ + rlim_t l = get( resource, hard ); + + if( l == RLIM_INFINITY ) + sb_append( sb_out, L"unlimited\n" ); + else + sb_printf( sb_out, L"%d\n", l ); + +} + + +static void print_all( int hard ) +{ + int i; + int w=0; + + for( i=0; resource_arr[i].desc; i++ ) + { + w=maxi( w, my_wcswidth(resource_arr[i].desc)); + } + + for( i=0; resource_arr[i].desc; i++ ) + { + struct rlimit ls; + rlim_t l; + getrlimit( resource_arr[i].resource, &ls ); + l = hard ? ls.rlim_max:ls.rlim_cur; + + wchar_t *unit = ((resource_arr[i].resource==RLIMIT_CPU)?L"(seconds, ":(get_multiplier(resource_arr[i].resource)==1?L"(":L"(kB, ")); + + sb_printf( sb_out, + L"%-*ls %10ls-%lc) ", + w, + resource_arr[i].desc, + unit, + resource_arr[i].switch_char); + + if( l == RLIM_INFINITY ) + sb_append( sb_out, L"unlimited\n" ); + else + sb_printf( sb_out, L"%d\n", l ); + } +} + +static const wchar_t *get_desc( int what ) +{ + int i; + + for( i=0; resource_arr[i].desc; i++ ) + { + if( resource_arr[i].resource == what ) + { + return resource_arr[i].desc; + } + } + return L"Not a resource"; +} + + +static int set( int resource, int hard, int soft, rlim_t value ) +{ + struct rlimit ls; + getrlimit( resource, &ls ); + if( value != RLIM_INFINITY ) + value *= get_multiplier( resource ); + + if( hard ) + { + ls.rlim_max = value; + } + + if( soft ) + { + ls.rlim_cur = value; + + /* + Do not attempt to set the soft limit higher than the hard limit + */ + if( ( value == RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY ) || + ( value != RLIM_INFINITY && ls.rlim_max != RLIM_INFINITY && value > ls.rlim_max)) + { + ls.rlim_cur = ls.rlim_max; + } + } + + if( setrlimit( resource, &ls ) ) + { + if( errno == EPERM ) + sb_printf( sb_err, L"ulimit: Permission denied when changing resource of type '%ls'\n", get_desc( resource ) ); + else + builtin_wperror( L"ulimit" ); + return 1; + } + return 0; +} + +static int set_all( int hard, int soft, rlim_t value ) +{ + int i; + int res=0; + + for( i=0; resource_arr[i].desc; i++ ) + { + if( set( resource_arr[i].resource, hard, soft, value ) ) + res = 1; + } + return res; +} + +int builtin_ulimit( wchar_t ** argv ) +{ + int hard=0; + int soft=0; + + int what = RLIMIT_FSIZE; + int report_all = 0; + + int argc = builtin_count_args( argv ); + + woptind=0; + + while( 1 ) + { + const static struct woption + long_options[] = + { + { + L"all", no_argument, 0, 'a' + } + , + { + L"hard", no_argument, 0, 'H' + } + , + { + L"soft", no_argument, 0, 'S' + } + , + { + L"core-size", no_argument, 0, 'c' + } + , + { + L"data-size", no_argument, 0, 'd' + } + , + { + L"file-size", no_argument, 0, 'f' + } + , + { + L"lock-size", no_argument, 0, 'l' + } + , + { + L"resident-set-size", no_argument, 0, 'm' + } + , + { + L"file-descriptor-count", no_argument, 0, 'n' + } + , + { + L"pipe-size", no_argument, 0, 'p' + } + , + { + L"cpu-time", no_argument, 0, 't' + } + , + { + L"process-count", no_argument, 0, 'u' + } + , + { + L"virtual-memory-size", no_argument, 0, 'v' + } + , + { + 0, 0, 0, 0 + } + } + ; + + + int opt_index = 0; + + int opt = wgetopt_long( argc, + argv, + L"aHScdflmnptuv", + long_options, + &opt_index ); + if( opt == -1 ) + break; + + switch( opt ) + { + case 0: + if(long_options[opt_index].flag != 0) + break; + sb_append2( sb_err, + argv[0], + BUILTIN_ERR_UNKNOWN, + L" ", + long_options[opt_index].name, + L"\n", + (void *)0 ); + builtin_print_help( argv[0], sb_err ); + + return 1; + + case L'a': + report_all=1; + break; + + case L'H': + hard=1; + break; + + case L'S': + soft=1; + break; + + case L'c': + what=RLIMIT_CORE; + break; + + case L'd': + what=RLIMIT_DATA; + break; + + case L'f': + what=RLIMIT_FSIZE; + break; + + case L'l': + what=RLIMIT_MEMLOCK; + break; + + case L'm': + what=RLIMIT_RSS; + break; + + case L'n': + what=RLIMIT_NOFILE; + break; + + case L's': + what=RLIMIT_STACK; + break; + + case L't': + what=RLIMIT_CPU; + break; + + case L'u': + what=RLIMIT_NPROC; + break; + + case L'v': + what=RLIMIT_AS; + break; + + case L'?': + builtin_print_help( argv[0], sb_err ); + return 1; + } + } + + switch( argc - woptind ) + { + case 0: + /* + Show current limit value + */ + if( report_all ) + { + print_all( hard ); + } + else + { + print( what, hard ); + } + + break; + + case 1: + { + /* + Change current limit value + */ + rlim_t new_limit; + wchar_t *end; + + /* + Set both hard and soft limits if nothing else was specified + */ + if( !(hard+soft) ) + { + hard=soft=1; + } + + + if( wcscasecmp( argv[woptind], L"unlimited" )==0) + { + new_limit = RLIM_INFINITY; + } + else if( wcscasecmp( argv[woptind], L"hard" )==0) + { + new_limit = get( what, 1 ); + } + else if( wcscasecmp( argv[woptind], L"soft" )==0) + { + new_limit = get( what, soft ); + } + else + { + errno=0; + new_limit = wcstol( argv[woptind], &end, 10 ); + if( errno || *end ) + { + sb_printf( sb_err, + L"%ls: Invalid limit '%ls'\n", + argv[0], + argv[woptind] ); + builtin_print_help( argv[0], sb_err ); + return 1; + } + } + + if( report_all ) + { + return set_all( hard, soft, new_limit ); + } + else + { + return set( what, hard, soft, new_limit ); + } + + + break; + } + + default: + sb_append2( sb_err, + argv[0], + L": Too many arguments\n", + (void *)0 ); + builtin_print_help( argv[0], sb_err ); + return 1; + + break; + } + return 0; +} diff --git a/wutil.c b/wutil.c index 7f8b09846..4256ba605 100644 --- a/wutil.c +++ b/wutil.c @@ -195,6 +195,19 @@ void wperror(const wchar_t *s) #if !HAVE_WPRINTF +void pad( void (*writer)(wchar_t), int count) +{ + + int i; + if( count < 0 ) + return; + + for( i=0; i= L'0') && (*filter <= L'9')) + { + width=10*width+(*filter++ - L'0'); + } + } while( loop ) { + switch(*filter) { case L'l': @@ -242,15 +267,21 @@ static int vgwprintf( void (*writer)(wchar_t), is_long++; filter++; break; + case L'*': /* Set minimum field width */ width = va_arg( va, int ); filter++; break; + + case L'-': + filter++; + pad_left=0; + break; + case L'.': /* Set precision. - Hasn't been tested enough yet, so I don't really trust it. */ filter++; if( *filter == L'*' ) @@ -272,30 +303,37 @@ static int vgwprintf( void (*writer)(wchar_t), break; } } + switch( *filter ) { case L'c': { wchar_t c; - - c = is_long?va_arg(va, wchar_t):btowc(va_arg(va, int)); - if( width>= 0 ) + + if( (width >= 0) && pad_left ) { - int i; - for( i=1; i= 0) && !pad_left ) + { + pad( writer, width-1 ); + count += maxi( width-1, 0 ); + } + count++; break; } case L's': { + wchar_t *ss=0; if( is_long ) { @@ -316,14 +354,10 @@ static int vgwprintf( void (*writer)(wchar_t), return -1; } - if( width>=0 ) + if( (width >= 0) && pad_left ) { - int i; - for( i=wcslen(ss); i 0) && (precision <= (count-precount) ) ) break; - + writer( *(s++) ); count++; } + if( (width >= 0) && !pad_left ) + { + pad( writer, width-wcslen(ss) ); + count += maxi( width-wcslen(ss), 0 ); + } + if( !is_long ) free( ss ); @@ -387,17 +427,12 @@ static int vgwprintf( void (*writer)(wchar_t), return -1; } - if( width >= 0 ) + if( (width >= 0) && pad_left ) { - int i; - - for( i=strlen(str); i= 0) && !pad_left ) + { + pad( writer, width-strlen(str) ); + count += maxi(width-strlen(str), 0 ); + } + break; } @@ -449,17 +490,13 @@ static int vgwprintf( void (*writer)(wchar_t), default: return -1; } - - if( width>=0 ) - { - int i; - for( i=strlen(str); i= 0) && pad_left ) + { + pad( writer, width-strlen(str) ); + count += maxi( width-strlen(str), 0 ); + } + pos = str; while( *pos ) @@ -468,6 +505,12 @@ static int vgwprintf( void (*writer)(wchar_t), count++; } + if( (width >= 0) && !pad_left ) + { + pad( writer, width-strlen(str) ); + count += maxi( width-strlen(str), 0 ); + } + break; }